xshell 1.0.112 → 1.0.114
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/file.d.ts +2 -2
- package/file.js +13 -6
- package/net.browser.d.ts +29 -17
- package/net.browser.js +101 -54
- package/net.d.ts +35 -21
- package/net.js +107 -57
- package/package.json +3 -3
- package/path.d.ts +2 -2
- package/utils.browser.d.ts +1 -1
- package/utils.d.ts +1 -1
package/file.d.ts
CHANGED
|
@@ -71,8 +71,8 @@ interface FWriteOptions {
|
|
|
71
71
|
- print?: `true` */
|
|
72
72
|
export declare function fwrite(fp: string, data: string | Uint8Array | any, options?: FWriteOptions): Promise<string>;
|
|
73
73
|
export declare function fwrite(fp: FileHandle, data: string | Uint8Array | any, options?: FWriteOptions): Promise<FileHandle>;
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
/** 追加内容到文件末尾,如果文件不存在会自动创建 */
|
|
75
|
+
export declare function fappend(fp: string, data: string | Uint8Array, { print }?: {
|
|
76
76
|
print?: boolean;
|
|
77
77
|
}): Promise<void>;
|
|
78
78
|
export type FStats = fs.BigIntStats & {
|
package/file.js
CHANGED
|
@@ -99,16 +99,23 @@ export async function fwrite(fp, data, { print = true } = {}) {
|
|
|
99
99
|
}
|
|
100
100
|
return fp;
|
|
101
101
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
assert(dir.isdir, t('dir 必须以 / 结尾'));
|
|
105
|
-
fp = dir + fp;
|
|
106
|
-
}
|
|
102
|
+
/** 追加内容到文件末尾,如果文件不存在会自动创建 */
|
|
103
|
+
export async function fappend(fp, data, { print = true } = {}) {
|
|
107
104
|
assert(path.isAbsolute(fp), `${t('fp 必须是绝对路径,当前为:')} ${fp}`);
|
|
108
105
|
if (print)
|
|
109
106
|
console.log(t('追加'), fp);
|
|
110
107
|
assert(isUint8Array(data) || typeof data === 'string');
|
|
111
|
-
|
|
108
|
+
try {
|
|
109
|
+
await fsp.appendFile(fp, data);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error.code === 'ENOENT') {
|
|
113
|
+
await fmkdir(fp.fdir, { print: false });
|
|
114
|
+
await fsp.appendFile(fp, data);
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
112
119
|
}
|
|
113
120
|
export async function flist(fpd, options = {}) {
|
|
114
121
|
const { filter, deep = false, absolute = false, print = true, stats = false } = options;
|
package/net.browser.d.ts
CHANGED
|
@@ -130,6 +130,23 @@ export interface Message<TData extends any[] = any[]> {
|
|
|
130
130
|
/** bins: data 中哪些下标对应的原始值是 Uint8Array 类型的,如: [0, 3] */
|
|
131
131
|
bins?: number[];
|
|
132
132
|
}
|
|
133
|
+
/** 自动保持 remote 连接,只对 initiator 且传入 url 有效,
|
|
134
|
+
- 在未连接,或 websocket on_error 检测到连接断开后以 reconnect_interval 间隔尝试 remote.call(func) 重连
|
|
135
|
+
- 在连接状态下以 heartbeat_interval 间隔发送心跳包确保连接状态
|
|
136
|
+
|
|
137
|
+
配置选项:
|
|
138
|
+
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
139
|
+
- args?: 连接后调用函数时传递的参数,与 func 配合使用
|
|
140
|
+
- reconnect_interval?: `1000 * 5` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
141
|
+
- heartbeat_interval?: `1000 * 60` 发送心跳包确保连接的间隔 (单位: ms)
|
|
142
|
+
- error_delay?: `2000` 遇到错误时首次尝试重连的延时 (单位: ms) */
|
|
143
|
+
export interface RemoteKeeperOptions {
|
|
144
|
+
func?: string;
|
|
145
|
+
args?: any[];
|
|
146
|
+
reconnect_interval?: number;
|
|
147
|
+
heartbeat_interval?: number;
|
|
148
|
+
error_delay?: number;
|
|
149
|
+
}
|
|
133
150
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
134
151
|
创建 remote 对象时传入 funcs 注册处理函数,使得对端能通过 (rpc message).func 调用
|
|
135
152
|
使用 remote.handle 方法处理对端发来的 websocket message,对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
|
|
@@ -167,22 +184,31 @@ export declare class Remote {
|
|
|
167
184
|
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
168
185
|
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
169
186
|
handlers: Map<number, MessageHandler<any[]>>;
|
|
187
|
+
keeper?: RemoteKeeperOptions;
|
|
170
188
|
/** `false` 打印所有交互的 rpc messages */
|
|
171
189
|
print: boolean;
|
|
190
|
+
first_error: boolean;
|
|
191
|
+
keeping: boolean;
|
|
172
192
|
reconnecting: boolean;
|
|
193
|
+
disconnected: boolean;
|
|
173
194
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
174
195
|
static parse<TData extends any[] = any[]>(buffer: Uint8Array): Message<TData>;
|
|
175
196
|
static pack({ id, func, data, done, error }: Message): Uint8Array;
|
|
176
197
|
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
177
198
|
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
178
|
-
constructor({ url, funcs, print, websocket, on_error }?: {
|
|
199
|
+
constructor({ url, funcs, print, websocket, keeper, on_error, }?: {
|
|
179
200
|
url?: string;
|
|
180
201
|
funcs?: Remote['funcs'];
|
|
181
202
|
print?: boolean;
|
|
182
203
|
websocket?: WebSocket;
|
|
183
|
-
|
|
204
|
+
keeper?: RemoteKeeperOptions;
|
|
205
|
+
on_error?(error: WebSocketConnectionError, websocket?: WebSocket): void;
|
|
184
206
|
});
|
|
185
|
-
|
|
207
|
+
/** 统一处理首次连接错误和连接后的错误 */
|
|
208
|
+
_on_error: (error: WebSocketConnectionError, websocket?: WebSocket) => void;
|
|
209
|
+
_on_message: (data: ArrayBuffer, websocket: WebSocket) => void;
|
|
210
|
+
/** 使用者自定义的在连接后出错时的处理 */
|
|
211
|
+
on_error?(error: WebSocketConnectionError, websocket?: WebSocket): void;
|
|
186
212
|
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
187
213
|
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
188
214
|
连接断开后,通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接,而通过传入 websocket 参数构造的实例只会检查连接状态,在断开时抛出异常
|
|
@@ -205,18 +231,4 @@ export declare class Remote {
|
|
|
205
231
|
作为 websocket 连接发起方,不需要传入 websocket
|
|
206
232
|
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
207
233
|
call<TReturn extends any[] = any[]>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
|
|
208
|
-
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
209
|
-
通常在 on_error 中和首次启动时调用
|
|
210
|
-
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
211
|
-
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
212
|
-
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
213
|
-
- on_error?: 接收每次尝试连接的错误 */
|
|
214
|
-
start_reconnecting({ func, args, interval, first_delay, on_error, }?: RemoteReconnectingOptions): Promise<void>;
|
|
215
|
-
}
|
|
216
|
-
export interface RemoteReconnectingOptions {
|
|
217
|
-
func?: string;
|
|
218
|
-
args?: any[];
|
|
219
|
-
interval?: number;
|
|
220
|
-
first_delay?: number;
|
|
221
|
-
on_error?(error: Error): void;
|
|
222
234
|
}
|
package/net.browser.js
CHANGED
|
@@ -277,9 +277,13 @@ export class Remote {
|
|
|
277
277
|
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
278
278
|
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
279
279
|
handlers = new Map();
|
|
280
|
+
keeper;
|
|
280
281
|
/** `false` 打印所有交互的 rpc messages */
|
|
281
282
|
print = false;
|
|
283
|
+
first_error = true;
|
|
284
|
+
keeping = false;
|
|
282
285
|
reconnecting = false;
|
|
286
|
+
disconnected = false;
|
|
283
287
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
284
288
|
static parse(buffer) {
|
|
285
289
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -300,7 +304,7 @@ export class Remote {
|
|
|
300
304
|
return message;
|
|
301
305
|
}
|
|
302
306
|
static pack({ id, func, data = [], done, error }) {
|
|
303
|
-
assert('length' in data,
|
|
307
|
+
assert('length' in data, 'message.data 必须是数组');
|
|
304
308
|
let data_ = new Array(data.length);
|
|
305
309
|
let bins = [];
|
|
306
310
|
let bufs = [];
|
|
@@ -330,22 +334,68 @@ export class Remote {
|
|
|
330
334
|
}
|
|
331
335
|
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
332
336
|
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
333
|
-
constructor({ url, funcs, print, websocket, on_error } = {}) {
|
|
334
|
-
assert(!(url && websocket),
|
|
337
|
+
constructor({ url, funcs, print, websocket, keeper, on_error, } = {}) {
|
|
338
|
+
assert(!(url && websocket), '构建 Remote 时 url 和 websocket 最多只能传一个');
|
|
335
339
|
this.initiator = Boolean(url || websocket);
|
|
336
340
|
if (url)
|
|
337
341
|
this.url = url;
|
|
338
|
-
|
|
339
|
-
|
|
342
|
+
assert(!funcs?.echo);
|
|
343
|
+
this.funcs = this.initiator ? funcs : {
|
|
344
|
+
...funcs,
|
|
345
|
+
echo({ data }) {
|
|
346
|
+
return data;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
340
349
|
if (print !== undefined)
|
|
341
350
|
this.print = print;
|
|
342
351
|
if (on_error)
|
|
343
352
|
this.on_error = on_error;
|
|
344
353
|
if (this.initiator)
|
|
345
354
|
this.lwebsocket = new Lock(websocket || null);
|
|
355
|
+
if (keeper) {
|
|
356
|
+
assert(this.initiator && url);
|
|
357
|
+
this.keeper = {
|
|
358
|
+
reconnect_interval: 1000 * 5,
|
|
359
|
+
heartbeat_interval: 1000 * 60,
|
|
360
|
+
error_delay: 1000 * 2,
|
|
361
|
+
...keeper
|
|
362
|
+
};
|
|
363
|
+
}
|
|
346
364
|
}
|
|
365
|
+
/** 统一处理首次连接错误和连接后的错误 */
|
|
366
|
+
_on_error = (error, websocket) => {
|
|
367
|
+
if (websocket) // 连接后出现的错误
|
|
368
|
+
this.on_error?.(error, websocket);
|
|
369
|
+
if (this.keeper && !this.reconnecting && !this.disconnected) // 在一段时间后调度错误重连
|
|
370
|
+
(async () => {
|
|
371
|
+
this.reconnecting = true;
|
|
372
|
+
const { error_delay, reconnect_interval } = this.keeper;
|
|
373
|
+
if (this.first_error) {
|
|
374
|
+
this.first_error = false;
|
|
375
|
+
await delay(error_delay);
|
|
376
|
+
}
|
|
377
|
+
else
|
|
378
|
+
await delay(reconnect_interval);
|
|
379
|
+
this.reconnecting = false;
|
|
380
|
+
if (!this.disconnected)
|
|
381
|
+
try {
|
|
382
|
+
await this.connect();
|
|
383
|
+
this.first_error = true;
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
// 错误由 this.connect 里面处理
|
|
387
|
+
}
|
|
388
|
+
})();
|
|
389
|
+
};
|
|
390
|
+
_on_message = (data, websocket) => {
|
|
391
|
+
this.handle(new Uint8Array(data), websocket);
|
|
392
|
+
};
|
|
393
|
+
/** 使用者自定义的在连接后出错时的处理 */
|
|
347
394
|
on_error(error, websocket) {
|
|
348
|
-
|
|
395
|
+
if (this.keeper)
|
|
396
|
+
console.log(error.message);
|
|
397
|
+
else
|
|
398
|
+
throw error;
|
|
349
399
|
}
|
|
350
400
|
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
351
401
|
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
@@ -357,28 +407,58 @@ export class Remote {
|
|
|
357
407
|
if (this.lwebsocket.resource?.readyState === WebSocket.OPEN)
|
|
358
408
|
return;
|
|
359
409
|
else if (!this.url)
|
|
360
|
-
throw new Error(
|
|
361
|
-
else
|
|
410
|
+
throw new Error('创建 Remote 时传入的 websocket 连接已断开');
|
|
411
|
+
else {
|
|
412
|
+
let reconnected = false;
|
|
362
413
|
// 假设有多个请求想要并发连接 websocket, 且此时 websocket 是断开的状态
|
|
363
414
|
// 应该排队依次连接,而不是后续的连接直接使用第一次连接的 promise,后续调用还是应该尝试重连(不止连接一次)
|
|
364
|
-
|
|
415
|
+
await this.lwebsocket.request(async (websocket) => {
|
|
365
416
|
// 保存的 rpc 状态在 this.handlers, 与 websocket 无关,因此即使断开重连也不影响 rpc 的运行,即
|
|
366
417
|
// 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
|
|
367
|
-
if (websocket?.readyState
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
418
|
+
if (websocket?.readyState !== WebSocket.OPEN) { // 重连
|
|
419
|
+
try {
|
|
420
|
+
this.lwebsocket.resource = await connect_websocket(this.url, {
|
|
421
|
+
on_message: this._on_message,
|
|
422
|
+
on_error: this._on_error
|
|
423
|
+
});
|
|
424
|
+
reconnected = true;
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
this._on_error(error);
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
376
431
|
});
|
|
432
|
+
if (this.keeper) {
|
|
433
|
+
const { heartbeat_interval, func, args } = this.keeper;
|
|
434
|
+
// 首次连接成功时,开始心跳保活
|
|
435
|
+
if (!this.keeping) {
|
|
436
|
+
this.keeping = true;
|
|
437
|
+
(async () => {
|
|
438
|
+
for (;;) {
|
|
439
|
+
await delay(heartbeat_interval);
|
|
440
|
+
if (this.disconnected)
|
|
441
|
+
break;
|
|
442
|
+
if (!this.reconnecting)
|
|
443
|
+
try {
|
|
444
|
+
await this.call('echo', []);
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
this._on_error(error);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
})();
|
|
451
|
+
}
|
|
452
|
+
if (reconnected && func)
|
|
453
|
+
await this.call(func, args);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
377
456
|
else if (websocket.readyState !== WebSocket.OPEN)
|
|
378
|
-
throw new Error(
|
|
457
|
+
throw new Error('传入的 websocket 连接已断开');
|
|
379
458
|
}
|
|
380
459
|
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
381
460
|
disconnect() {
|
|
461
|
+
this.disconnected = true;
|
|
382
462
|
this.lwebsocket.resource?.close(1000);
|
|
383
463
|
}
|
|
384
464
|
/** 发送 message 到对端 remote
|
|
@@ -386,7 +466,7 @@ export class Remote {
|
|
|
386
466
|
作为 websocket 连接接收方,必传 websocket 参数
|
|
387
467
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
388
468
|
async send(message, websocket) {
|
|
389
|
-
assert(!message.data || message.data.every(arg => arg !== undefined),
|
|
469
|
+
assert(!message.data || message.data.every(arg => arg !== undefined), `message.data 数组中不能有 undefined 的项, 因为 json 序列化后会变为 null. (func: ${message.func}, id: ${message.id})`);
|
|
390
470
|
if (this.print)
|
|
391
471
|
console.log('remote.send:', message);
|
|
392
472
|
try {
|
|
@@ -425,7 +505,7 @@ export class Remote {
|
|
|
425
505
|
await this.send({ id, data }, websocket);
|
|
426
506
|
}
|
|
427
507
|
else
|
|
428
|
-
throw message.error || new Error(
|
|
508
|
+
throw message.error || new Error(`找不到 rpc handler: ${func ? `func: ${func.quote()}` : `id: ${id}`}`);
|
|
429
509
|
}
|
|
430
510
|
catch (error) {
|
|
431
511
|
// handler 出错并不意味着 rpc 一定会结束,可能 error 是运行中的正常数据,所以不能清理 handler
|
|
@@ -458,38 +538,5 @@ export class Remote {
|
|
|
458
538
|
}
|
|
459
539
|
});
|
|
460
540
|
}
|
|
461
|
-
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
462
|
-
通常在 on_error 中和首次启动时调用
|
|
463
|
-
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
464
|
-
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
465
|
-
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
466
|
-
- on_error?: 接收每次尝试连接的错误 */
|
|
467
|
-
async start_reconnecting({ func, args, interval = 1000 * 10, first_delay = 0, on_error, } = {}) {
|
|
468
|
-
assert(this.url);
|
|
469
|
-
if (this.reconnecting)
|
|
470
|
-
return;
|
|
471
|
-
this.reconnecting = true;
|
|
472
|
-
if (first_delay)
|
|
473
|
-
await delay(first_delay);
|
|
474
|
-
for (;;) {
|
|
475
|
-
if (this.lwebsocket.resource?.readyState !== WebSocket.OPEN)
|
|
476
|
-
try {
|
|
477
|
-
if (func)
|
|
478
|
-
await this.call(func, args);
|
|
479
|
-
else
|
|
480
|
-
await this.connect();
|
|
481
|
-
this.reconnecting = false;
|
|
482
|
-
break;
|
|
483
|
-
}
|
|
484
|
-
catch (error) {
|
|
485
|
-
on_error?.(error);
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
488
|
-
this.reconnecting = false;
|
|
489
|
-
break;
|
|
490
|
-
}
|
|
491
|
-
await delay(interval);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
541
|
}
|
|
495
542
|
//# sourceMappingURL=net.browser.js.map
|
package/net.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
-
import type
|
|
3
|
+
import { type Readable } from 'stream';
|
|
4
4
|
import type { FormData } from 'undici';
|
|
5
5
|
import type { WebSocket, CloseEvent, ErrorEvent } from 'ws';
|
|
6
6
|
import type { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
|
|
@@ -20,7 +20,7 @@ export declare const WebSocketClosed = 3;
|
|
|
20
20
|
export declare enum MyProxy {
|
|
21
21
|
socks5 = "http://localhost:10080",
|
|
22
22
|
whistle = "http://localhost:8899",
|
|
23
|
-
work = "http://
|
|
23
|
+
work = "http://localhost:10090"
|
|
24
24
|
}
|
|
25
25
|
export declare const cookies: {
|
|
26
26
|
store: MemoryCookieStore;
|
|
@@ -51,7 +51,7 @@ export interface RequestOptions {
|
|
|
51
51
|
method?: 'GET' | 'POST' | 'PUT' | 'HEAD' | 'DELETE' | 'PATCH';
|
|
52
52
|
queries?: Record<string, any>;
|
|
53
53
|
headers?: Record<string, string>;
|
|
54
|
-
body?: string | Record<string, any> | Uint8Array | URLSearchParams | FormData;
|
|
54
|
+
body?: string | Record<string, any> | Uint8Array | URLSearchParams | FormData | Readable;
|
|
55
55
|
type?: 'application/json' | 'application/x-www-form-urlencoded' | 'multipart/form-data';
|
|
56
56
|
proxy?: boolean | MyProxy | string;
|
|
57
57
|
encoding?: Encoding | 'binary';
|
|
@@ -60,6 +60,7 @@ export interface RequestOptions {
|
|
|
60
60
|
auth?: BasicAuth | BearerAuth;
|
|
61
61
|
cookies?: Record<string, string>;
|
|
62
62
|
redirect?: RequestRedirect;
|
|
63
|
+
decode?: boolean;
|
|
63
64
|
}
|
|
64
65
|
export interface RequestFullOptions extends RequestOptions {
|
|
65
66
|
full: true;
|
|
@@ -97,7 +98,8 @@ export interface RequestError extends Error {
|
|
|
97
98
|
- auth?: BasicAuth | BearerAuth
|
|
98
99
|
- cookies?: 需要额外添加到请求的 cookies, 默认情况下携带了 MemoryCookieStore 中保存的之前 http 响应中的 cookies
|
|
99
100
|
- raw?: `false` 传入后返回整个 response (RawResponse),body 为 Readable
|
|
100
|
-
- full?: `false` 传入后返回整个 response (FullResponse),body 根据 encoding 对应为 string 或 Buffer
|
|
101
|
+
- full?: `false` 传入后返回整个 response (FullResponse),body 根据 encoding 对应为 string 或 Buffer
|
|
102
|
+
- decode?: 根据 content-encoding: br 等解码压缩后的 response.body */
|
|
101
103
|
export declare function request(url: string | URL): Promise<string>;
|
|
102
104
|
export declare function request(url: string | URL, options: RequestRawOptions): Promise<RawResponse>;
|
|
103
105
|
export declare function request(url: string | URL, options: RequestFullOptions & {
|
|
@@ -195,6 +197,23 @@ export interface Message<TData extends any[] = any[]> {
|
|
|
195
197
|
/** bins: data 中哪些下标对应的原始值是 Uint8Array 类型的,如: [0, 3] */
|
|
196
198
|
bins?: number[];
|
|
197
199
|
}
|
|
200
|
+
/** 自动保持 remote 连接,只对 initiator 且传入 url 有效,
|
|
201
|
+
- 在未连接,或 websocket on_error 检测到连接断开后以 reconnect_interval 间隔尝试 remote.call(func) 重连
|
|
202
|
+
- 在连接状态下以 heartbeat_interval 间隔发送心跳包确保连接状态
|
|
203
|
+
|
|
204
|
+
配置选项:
|
|
205
|
+
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
206
|
+
- args?: 连接后调用函数时传递的参数,与 func 配合使用
|
|
207
|
+
- reconnect_interval?: `1000 * 5` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
208
|
+
- heartbeat_interval?: `1000 * 60` 发送心跳包确保连接的间隔 (单位: ms)
|
|
209
|
+
- error_delay?: `2000` 遇到错误时首次尝试重连的延时 (单位: ms) */
|
|
210
|
+
export interface RemoteKeeperOptions {
|
|
211
|
+
func?: string;
|
|
212
|
+
args?: any[];
|
|
213
|
+
reconnect_interval?: number;
|
|
214
|
+
heartbeat_interval?: number;
|
|
215
|
+
error_delay?: number;
|
|
216
|
+
}
|
|
198
217
|
/** 通过创建 remote 对象对 websocket rpc 进行抽象
|
|
199
218
|
创建 remote 对象时传入 funcs 注册处理函数,使得对端能通过 (rpc message).func 调用
|
|
200
219
|
使用 remote.handle 方法处理对端发来的 websocket message,对于 websocket 连接接收方需要手动绑定 websocket message 事件到 remote.handle
|
|
@@ -232,22 +251,31 @@ export declare class Remote {
|
|
|
232
251
|
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
233
252
|
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
234
253
|
handlers: Map<number, MessageHandler<any[]>>;
|
|
254
|
+
keeper?: RemoteKeeperOptions;
|
|
235
255
|
/** `false` 打印所有交互的 rpc messages */
|
|
236
256
|
print: boolean;
|
|
257
|
+
first_error: boolean;
|
|
258
|
+
keeping: boolean;
|
|
237
259
|
reconnecting: boolean;
|
|
260
|
+
disconnected: boolean;
|
|
238
261
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
239
262
|
static parse<TData extends any[] = any[]>(buffer: Uint8Array): Message<TData>;
|
|
240
263
|
static pack({ id, func, data, done, error }: Message): Uint8Array;
|
|
241
264
|
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
242
265
|
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
243
|
-
constructor({ url, funcs, print, websocket, on_error }?: {
|
|
266
|
+
constructor({ url, funcs, print, websocket, keeper, on_error, }?: {
|
|
244
267
|
url?: string;
|
|
245
268
|
funcs?: Remote['funcs'];
|
|
246
269
|
print?: boolean;
|
|
247
270
|
websocket?: WebSocket;
|
|
248
|
-
|
|
271
|
+
keeper?: RemoteKeeperOptions;
|
|
272
|
+
on_error?(error: WebSocketConnectionError, websocket?: WebSocket): void;
|
|
249
273
|
});
|
|
250
|
-
|
|
274
|
+
/** 统一处理首次连接错误和连接后的错误 */
|
|
275
|
+
_on_error: (error: WebSocketConnectionError, websocket?: WebSocket) => void;
|
|
276
|
+
_on_message: (data: ArrayBuffer, websocket: WebSocket) => void;
|
|
277
|
+
/** 使用者自定义的在连接后出错时的处理 */
|
|
278
|
+
on_error?(error: WebSocketConnectionError, websocket?: WebSocket): void;
|
|
251
279
|
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
252
280
|
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
253
281
|
连接断开后,通过传入 url 参数构造的 remote 实例会自动创建新的 websocket 连接,而通过传入 websocket 参数构造的实例只会检查连接状态,在断开时抛出异常
|
|
@@ -270,13 +298,6 @@ export declare class Remote {
|
|
|
270
298
|
作为 websocket 连接发起方,不需要传入 websocket
|
|
271
299
|
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
272
300
|
call<TReturn extends any[] = any[]>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
|
|
273
|
-
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
274
|
-
通常在 on_error 中和首次启动时调用
|
|
275
|
-
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
276
|
-
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
277
|
-
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
278
|
-
- on_error?: 接收每次尝试连接的错误 */
|
|
279
|
-
start_reconnecting({ func, args, interval, first_delay, on_error, }?: RemoteReconnectingOptions): Promise<void>;
|
|
280
301
|
}
|
|
281
302
|
/** 为连接到 server 的 client 创建 RemoteClient,以便后续调用 client 中方法 */
|
|
282
303
|
export declare class RemoteClient {
|
|
@@ -289,10 +310,3 @@ export declare class RemoteClient {
|
|
|
289
310
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
290
311
|
send(message: Message): Promise<void>;
|
|
291
312
|
}
|
|
292
|
-
export interface RemoteReconnectingOptions {
|
|
293
|
-
func?: string;
|
|
294
|
-
args?: any[];
|
|
295
|
-
interval?: number;
|
|
296
|
-
first_delay?: number;
|
|
297
|
-
on_error?(error: Error): void;
|
|
298
|
-
}
|
package/net.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import zlib from 'zlib';
|
|
2
2
|
import { buffer as stream_to_buffer, text as stream_to_text } from 'stream/consumers';
|
|
3
|
+
import { isReadable } from 'stream';
|
|
3
4
|
import { t } from './i18n/instance.js';
|
|
4
5
|
import './prototype.js';
|
|
5
6
|
import { inspect, concat, assert, genid, delay, Lock, encode, decode, pipe_with_error, map_values, unique } from './utils.js';
|
|
@@ -11,7 +12,7 @@ export var MyProxy;
|
|
|
11
12
|
(function (MyProxy) {
|
|
12
13
|
MyProxy["socks5"] = "http://localhost:10080";
|
|
13
14
|
MyProxy["whistle"] = "http://localhost:8899";
|
|
14
|
-
MyProxy["work"] = "http://
|
|
15
|
+
MyProxy["work"] = "http://localhost:10090";
|
|
15
16
|
})(MyProxy || (MyProxy = {}));
|
|
16
17
|
// ------------------------------------ fetch, request
|
|
17
18
|
export const cookies = {
|
|
@@ -62,7 +63,7 @@ export async function request(url, options = {}) {
|
|
|
62
63
|
const { ProxyAgent: UndiciProxyAgent, FormData } = await import('undici');
|
|
63
64
|
const { Cookie } = await import('tough-cookie');
|
|
64
65
|
await cookies.init();
|
|
65
|
-
const { queries, headers: _headers, body, type = 'application/json', timeout = 5 * 1000, auth, cookies: _cookies, raw = false, full = false, redirect = 'follow', } = options;
|
|
66
|
+
const { queries, headers: _headers, body, type = 'application/json', timeout = 5 * 1000, auth, cookies: _cookies, raw = false, full = false, redirect = 'follow', decode = true, } = options;
|
|
66
67
|
let { method, retries, encoding, proxy, } = options;
|
|
67
68
|
url = new URL(url);
|
|
68
69
|
if (queries)
|
|
@@ -133,6 +134,8 @@ export async function request(url, options = {}) {
|
|
|
133
134
|
body: (() => {
|
|
134
135
|
if (body === undefined)
|
|
135
136
|
return;
|
|
137
|
+
if (typeof body?.read === 'function' && isReadable(body))
|
|
138
|
+
return body;
|
|
136
139
|
switch (type) {
|
|
137
140
|
case 'application/json': // 可能的类型 string | Record<string, any> | Uint8Array
|
|
138
141
|
if (typeof body === 'string')
|
|
@@ -169,7 +172,7 @@ export async function request(url, options = {}) {
|
|
|
169
172
|
// UndiciResponse.body 没有自动根据 content-encoding 来解码,这里手动处理并替换 body 为解码后的 stream
|
|
170
173
|
let body = _body;
|
|
171
174
|
const content_encoding = headers['content-encoding']?.trim().toLowerCase();
|
|
172
|
-
if (content_encoding) {
|
|
175
|
+
if (decode && content_encoding) {
|
|
173
176
|
assert(!content_encoding.includes(','));
|
|
174
177
|
switch (content_encoding) {
|
|
175
178
|
case 'gzip':
|
|
@@ -485,9 +488,13 @@ export class Remote {
|
|
|
485
488
|
/** map<id, message handler>: 通过 (rpc message).id 找到对应的 handler
|
|
486
489
|
一元 rpc 接收方不需要设置 handlers, 发送方需要 */
|
|
487
490
|
handlers = new Map();
|
|
491
|
+
keeper;
|
|
488
492
|
/** `false` 打印所有交互的 rpc messages */
|
|
489
493
|
print = false;
|
|
494
|
+
first_error = true;
|
|
495
|
+
keeping = false;
|
|
490
496
|
reconnecting = false;
|
|
497
|
+
disconnected = false;
|
|
491
498
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
492
499
|
static parse(buffer) {
|
|
493
500
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -508,7 +515,7 @@ export class Remote {
|
|
|
508
515
|
return message;
|
|
509
516
|
}
|
|
510
517
|
static pack({ id, func, data = [], done, error }) {
|
|
511
|
-
assert('length' in data,
|
|
518
|
+
assert('length' in data, 'message.data 必须是数组');
|
|
512
519
|
let data_ = new Array(data.length);
|
|
513
520
|
let bins = [];
|
|
514
521
|
let bufs = [];
|
|
@@ -538,22 +545,68 @@ export class Remote {
|
|
|
538
545
|
}
|
|
539
546
|
/** 作为 websocket 连接发起方,传入 url 或 websocket,定义远程 Remote
|
|
540
547
|
作为 websocket 连接接收方,不传 url 和 websocket,定义本地 Remote */
|
|
541
|
-
constructor({ url, funcs, print, websocket, on_error } = {}) {
|
|
542
|
-
assert(!(url && websocket),
|
|
548
|
+
constructor({ url, funcs, print, websocket, keeper, on_error, } = {}) {
|
|
549
|
+
assert(!(url && websocket), '构建 Remote 时 url 和 websocket 最多只能传一个');
|
|
543
550
|
this.initiator = Boolean(url || websocket);
|
|
544
551
|
if (url)
|
|
545
552
|
this.url = url;
|
|
546
|
-
|
|
547
|
-
|
|
553
|
+
assert(!funcs?.echo);
|
|
554
|
+
this.funcs = this.initiator ? funcs : {
|
|
555
|
+
...funcs,
|
|
556
|
+
echo({ data }) {
|
|
557
|
+
return data;
|
|
558
|
+
}
|
|
559
|
+
};
|
|
548
560
|
if (print !== undefined)
|
|
549
561
|
this.print = print;
|
|
550
562
|
if (on_error)
|
|
551
563
|
this.on_error = on_error;
|
|
552
564
|
if (this.initiator)
|
|
553
565
|
this.lwebsocket = new Lock(websocket || null);
|
|
566
|
+
if (keeper) {
|
|
567
|
+
assert(this.initiator && url);
|
|
568
|
+
this.keeper = {
|
|
569
|
+
reconnect_interval: 1000 * 5,
|
|
570
|
+
heartbeat_interval: 1000 * 60,
|
|
571
|
+
error_delay: 1000 * 2,
|
|
572
|
+
...keeper
|
|
573
|
+
};
|
|
574
|
+
}
|
|
554
575
|
}
|
|
576
|
+
/** 统一处理首次连接错误和连接后的错误 */
|
|
577
|
+
_on_error = (error, websocket) => {
|
|
578
|
+
if (websocket) // 连接后出现的错误
|
|
579
|
+
this.on_error?.(error, websocket);
|
|
580
|
+
if (this.keeper && !this.reconnecting && !this.disconnected) // 在一段时间后调度错误重连
|
|
581
|
+
(async () => {
|
|
582
|
+
this.reconnecting = true;
|
|
583
|
+
const { error_delay, reconnect_interval } = this.keeper;
|
|
584
|
+
if (this.first_error) {
|
|
585
|
+
this.first_error = false;
|
|
586
|
+
await delay(error_delay);
|
|
587
|
+
}
|
|
588
|
+
else
|
|
589
|
+
await delay(reconnect_interval);
|
|
590
|
+
this.reconnecting = false;
|
|
591
|
+
if (!this.disconnected)
|
|
592
|
+
try {
|
|
593
|
+
await this.connect();
|
|
594
|
+
this.first_error = true;
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
// 错误由 this.connect 里面处理
|
|
598
|
+
}
|
|
599
|
+
})();
|
|
600
|
+
};
|
|
601
|
+
_on_message = (data, websocket) => {
|
|
602
|
+
this.handle(new Uint8Array(data), websocket);
|
|
603
|
+
};
|
|
604
|
+
/** 使用者自定义的在连接后出错时的处理 */
|
|
555
605
|
on_error(error, websocket) {
|
|
556
|
-
|
|
606
|
+
if (this.keeper)
|
|
607
|
+
console.log(error.message);
|
|
608
|
+
else
|
|
609
|
+
throw error;
|
|
557
610
|
}
|
|
558
611
|
/** 幂等,保证 websocket 已连接,否则抛出异常
|
|
559
612
|
一般情况不需要手动调用,在其它方法中会自动调用这个方法,除非需要手动建立 websocket 连接并确保成功
|
|
@@ -565,28 +618,58 @@ export class Remote {
|
|
|
565
618
|
if (this.lwebsocket.resource?.readyState === WebSocketOpen)
|
|
566
619
|
return;
|
|
567
620
|
else if (!this.url)
|
|
568
|
-
throw new Error(
|
|
569
|
-
else
|
|
621
|
+
throw new Error('创建 Remote 时传入的 websocket 连接已断开');
|
|
622
|
+
else {
|
|
623
|
+
let reconnected = false;
|
|
570
624
|
// 假设有多个请求想要并发连接 websocket, 且此时 websocket 是断开的状态
|
|
571
625
|
// 应该排队依次连接,而不是后续的连接直接使用第一次连接的 promise,后续调用还是应该尝试重连(不止连接一次)
|
|
572
|
-
|
|
626
|
+
await this.lwebsocket.request(async (websocket) => {
|
|
573
627
|
// 保存的 rpc 状态在 this.handlers, 与 websocket 无关,因此即使断开重连也不影响 rpc 的运行,即
|
|
574
628
|
// 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
|
|
575
|
-
if (websocket?.readyState
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
629
|
+
if (websocket?.readyState !== WebSocketOpen) { // 重连
|
|
630
|
+
try {
|
|
631
|
+
this.lwebsocket.resource = await connect_websocket(this.url, {
|
|
632
|
+
on_message: this._on_message,
|
|
633
|
+
on_error: this._on_error
|
|
634
|
+
});
|
|
635
|
+
reconnected = true;
|
|
636
|
+
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
this._on_error(error);
|
|
639
|
+
throw error;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
584
642
|
});
|
|
643
|
+
if (this.keeper) {
|
|
644
|
+
const { heartbeat_interval, func, args } = this.keeper;
|
|
645
|
+
// 首次连接成功时,开始心跳保活
|
|
646
|
+
if (!this.keeping) {
|
|
647
|
+
this.keeping = true;
|
|
648
|
+
(async () => {
|
|
649
|
+
for (;;) {
|
|
650
|
+
await delay(heartbeat_interval);
|
|
651
|
+
if (this.disconnected)
|
|
652
|
+
break;
|
|
653
|
+
if (!this.reconnecting)
|
|
654
|
+
try {
|
|
655
|
+
await this.call('echo', []);
|
|
656
|
+
}
|
|
657
|
+
catch (error) {
|
|
658
|
+
this._on_error(error);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
})();
|
|
662
|
+
}
|
|
663
|
+
if (reconnected && func)
|
|
664
|
+
await this.call(func, args);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
585
667
|
else if (websocket.readyState !== WebSocketOpen)
|
|
586
|
-
throw new Error(
|
|
668
|
+
throw new Error('传入的 websocket 连接已断开');
|
|
587
669
|
}
|
|
588
670
|
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
589
671
|
disconnect() {
|
|
672
|
+
this.disconnected = true;
|
|
590
673
|
this.lwebsocket.resource?.close(1000);
|
|
591
674
|
}
|
|
592
675
|
/** 发送 message 到对端 remote
|
|
@@ -594,7 +677,7 @@ export class Remote {
|
|
|
594
677
|
作为 websocket 连接接收方,必传 websocket 参数
|
|
595
678
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
596
679
|
async send(message, websocket) {
|
|
597
|
-
assert(!message.data || message.data.every(arg => arg !== undefined),
|
|
680
|
+
assert(!message.data || message.data.every(arg => arg !== undefined), `message.data 数组中不能有 undefined 的项, 因为 json 序列化后会变为 null. (func: ${message.func}, id: ${message.id})`);
|
|
598
681
|
if (this.print)
|
|
599
682
|
console.log('remote.send:', message);
|
|
600
683
|
try {
|
|
@@ -633,7 +716,7 @@ export class Remote {
|
|
|
633
716
|
await this.send({ id, data }, websocket);
|
|
634
717
|
}
|
|
635
718
|
else
|
|
636
|
-
throw message.error || new Error(
|
|
719
|
+
throw message.error || new Error(`找不到 rpc handler: ${func ? `func: ${func.quote()}` : `id: ${id}`}`);
|
|
637
720
|
}
|
|
638
721
|
catch (error) {
|
|
639
722
|
// handler 出错并不意味着 rpc 一定会结束,可能 error 是运行中的正常数据,所以不能清理 handler
|
|
@@ -666,39 +749,6 @@ export class Remote {
|
|
|
666
749
|
}
|
|
667
750
|
});
|
|
668
751
|
}
|
|
669
|
-
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
670
|
-
通常在 on_error 中和首次启动时调用
|
|
671
|
-
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
672
|
-
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
673
|
-
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
674
|
-
- on_error?: 接收每次尝试连接的错误 */
|
|
675
|
-
async start_reconnecting({ func, args, interval = 1000 * 10, first_delay = 0, on_error, } = {}) {
|
|
676
|
-
assert(this.url);
|
|
677
|
-
if (this.reconnecting)
|
|
678
|
-
return;
|
|
679
|
-
this.reconnecting = true;
|
|
680
|
-
if (first_delay)
|
|
681
|
-
await delay(first_delay);
|
|
682
|
-
for (;;) {
|
|
683
|
-
if (this.lwebsocket.resource?.readyState !== WebSocketOpen)
|
|
684
|
-
try {
|
|
685
|
-
if (func)
|
|
686
|
-
await this.call(func, args);
|
|
687
|
-
else
|
|
688
|
-
await this.connect();
|
|
689
|
-
this.reconnecting = false;
|
|
690
|
-
break;
|
|
691
|
-
}
|
|
692
|
-
catch (error) {
|
|
693
|
-
on_error?.(error);
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
this.reconnecting = false;
|
|
697
|
-
break;
|
|
698
|
-
}
|
|
699
|
-
await delay(interval);
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
752
|
}
|
|
703
753
|
/** 为连接到 server 的 client 创建 RemoteClient,以便后续调用 client 中方法 */
|
|
704
754
|
export class RemoteClient {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.114",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"tslib": "^2.6.2",
|
|
100
100
|
"typescript": "^5.4.5",
|
|
101
101
|
"ua-parser-js": "^2.0.0-beta.2",
|
|
102
|
-
"undici": "^6.18.
|
|
102
|
+
"undici": "^6.18.2",
|
|
103
103
|
"vinyl": "^3.0.0",
|
|
104
104
|
"vinyl-fs": "^4.0.0",
|
|
105
105
|
"ws": "^8.17.0"
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"@types/koa-compress": "^4.0.6",
|
|
119
119
|
"@types/lodash": "^4.17.4",
|
|
120
120
|
"@types/mime-types": "^2.1.4",
|
|
121
|
-
"@types/node": "^20.12.
|
|
121
|
+
"@types/node": "^20.12.13",
|
|
122
122
|
"@types/react": "^18.3.3",
|
|
123
123
|
"@types/through2": "^2.0.41",
|
|
124
124
|
"@types/tough-cookie": "^4.0.5",
|
package/path.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export declare function extname(path: string): string;
|
|
|
54
54
|
/** `/` */
|
|
55
55
|
export declare const sep = "/";
|
|
56
56
|
/** The platform-specific file delimiter. ';' or ':'. */
|
|
57
|
-
export declare const delimiter: "
|
|
57
|
+
export declare const delimiter: ";" | ":";
|
|
58
58
|
/** Returns an object from a path string - the opposite of format().
|
|
59
59
|
@param path path to evaluate.
|
|
60
60
|
@throws {TypeError} if `path` is not a string. */
|
|
@@ -81,7 +81,7 @@ export declare let path: {
|
|
|
81
81
|
basename: typeof basename;
|
|
82
82
|
extname: typeof extname;
|
|
83
83
|
sep: string;
|
|
84
|
-
delimiter: "
|
|
84
|
+
delimiter: ";" | ":";
|
|
85
85
|
parse: typeof parse;
|
|
86
86
|
format: typeof format;
|
|
87
87
|
toNamespacedPath: typeof toNamespacedPath;
|
package/utils.browser.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export declare function encode(str: string): Uint8Array;
|
|
|
63
63
|
在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
|
|
64
64
|
export declare function decode(buffer: Uint8Array): string;
|
|
65
65
|
/** 字符串字典序比较 */
|
|
66
|
-
export declare function strcmp(l: string, r: string):
|
|
66
|
+
export declare function strcmp(l: string, r: string): 1 | 0 | -1;
|
|
67
67
|
/** 比较 1.10.02 这种版本号 */
|
|
68
68
|
export declare function vercmp(l: string, r: string): number;
|
|
69
69
|
export declare function get<TReturn = any>(obj: any, keypath: string): TReturn;
|
package/utils.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export declare function filter_values<TObj extends Record<string, any>>(obj: TOb
|
|
|
39
39
|
/** 忽略对象中的 keys, 返回新对象 */
|
|
40
40
|
export declare function omit<TObj>(obj: TObj, omit_keys: string[]): TObj;
|
|
41
41
|
/** 字符串字典序比较 */
|
|
42
|
-
export declare function strcmp(l: string, r: string):
|
|
42
|
+
export declare function strcmp(l: string, r: string): 1 | 0 | -1;
|
|
43
43
|
/** 比较 1.10.02 这种版本号 */
|
|
44
44
|
export declare function vercmp(l: string, r: string): number;
|
|
45
45
|
export declare function get<TReturn = any>(obj: any, keypath: string): TReturn;
|