xshell 1.0.46 → 1.0.48
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 +4 -2
- package/file.js +8 -7
- package/net.browser.d.ts +14 -0
- package/net.browser.js +34 -0
- package/net.d.ts +31 -7
- package/net.js +75 -15
- package/package.json +6 -8
- package/repl.d.ts +0 -19
- package/repl.js +1 -249
- package/server.d.ts +3 -3
- package/server.js +10 -12
- package/utils.browser.js +1 -1
- package/utils.js +1 -1
package/file.d.ts
CHANGED
|
@@ -155,6 +155,8 @@ export declare function flink(fp_real: string, fp_link: string, { junction, prin
|
|
|
155
155
|
junction?: boolean;
|
|
156
156
|
print?: boolean;
|
|
157
157
|
}): Promise<void>;
|
|
158
|
-
/** 打开一个文件并搜索替换某个 pattern
|
|
159
|
-
export declare function freplace(fp: string, pattern: string | RegExp, replacement: string
|
|
158
|
+
/** 打开一个文件并搜索替换某个 pattern */
|
|
159
|
+
export declare function freplace(fp: string, pattern: string | RegExp, replacement: string, { print }?: {
|
|
160
|
+
print?: boolean;
|
|
161
|
+
}): Promise<void>;
|
|
160
162
|
export {};
|
package/file.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { promises as fsp, default as fs } from 'fs';
|
|
2
2
|
import { isUint8Array } from 'util/types';
|
|
3
|
-
import fse from 'fs-extra';
|
|
4
3
|
import { path } from './path.js';
|
|
5
4
|
import { t } from './i18n/instance.js';
|
|
6
5
|
import { to_json } from './prototype.js';
|
|
@@ -209,7 +208,8 @@ export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, }
|
|
|
209
208
|
assert(path.isAbsolute(fp_src) && path.isAbsolute(fp_dst), t('fp_src 和 fp_dst 必须为完整路径'));
|
|
210
209
|
if (print)
|
|
211
210
|
console.log(t('复制'), fp_src, '→', fp_dst);
|
|
212
|
-
|
|
211
|
+
const { copy } = await import('fs-extra');
|
|
212
|
+
await copy(fp_src, fp_dst, { overwrite, errorOnExist: true });
|
|
213
213
|
}
|
|
214
214
|
/** 移动文件或文件夹 move file or direcotry
|
|
215
215
|
- src: 源 文件/文件夹 完整路径 src file/directory absolute path
|
|
@@ -223,7 +223,8 @@ export async function fmove(src, dst, { overwrite = false, print = true } = {})
|
|
|
223
223
|
throw new Error(t('src 和 dst 必须为完整路径'));
|
|
224
224
|
if (print)
|
|
225
225
|
console.log(t('移动'), src, '→', dst);
|
|
226
|
-
|
|
226
|
+
const { move } = await import('fs-extra');
|
|
227
|
+
await move(src, dst, { overwrite });
|
|
227
228
|
}
|
|
228
229
|
/** 重命名文件 rename file
|
|
229
230
|
- fp: 当前文件名/路径 current filename/path
|
|
@@ -293,9 +294,9 @@ export async function flink(fp_real, fp_link, { junction = false, print = true }
|
|
|
293
294
|
else
|
|
294
295
|
fsp.symlink(fp_real, fp_link, is_fpd_real ? 'dir' : 'file');
|
|
295
296
|
}
|
|
296
|
-
/** 打开一个文件并搜索替换某个 pattern
|
|
297
|
-
export async function freplace(fp, pattern, replacement) {
|
|
298
|
-
await fwrite(fp, (await fread(fp))
|
|
299
|
-
.replaceAll(pattern, replacement));
|
|
297
|
+
/** 打开一个文件并搜索替换某个 pattern */
|
|
298
|
+
export async function freplace(fp, pattern, replacement, { print = true } = {}) {
|
|
299
|
+
await fwrite(fp, (await fread(fp, { print }))
|
|
300
|
+
.replaceAll(pattern, replacement), { print });
|
|
300
301
|
}
|
|
301
302
|
//# sourceMappingURL=file.js.map
|
package/net.browser.d.ts
CHANGED
|
@@ -163,6 +163,7 @@ export declare class Remote {
|
|
|
163
163
|
handlers: Map<number, MessageHandler>;
|
|
164
164
|
/** `false` 打印所有交互的 rpc messages */
|
|
165
165
|
print: boolean;
|
|
166
|
+
reconnecting: boolean;
|
|
166
167
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
167
168
|
static parse<TData extends any[] = any[]>(buffer: Uint8Array): Message<TData>;
|
|
168
169
|
static pack({ id, func, data, done, error }: Message): Uint8Array;
|
|
@@ -198,4 +199,17 @@ export declare class Remote {
|
|
|
198
199
|
作为 websocket 连接发起方,不需要传入 websocket
|
|
199
200
|
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
200
201
|
call<TReturn extends any[] = any[]>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
|
|
202
|
+
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
203
|
+
通常在 on_error 中和首次启动时调用
|
|
204
|
+
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
205
|
+
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
206
|
+
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
207
|
+
- on_error?: 接收每次尝试连接的错误 */
|
|
208
|
+
start_reconnecting({ func, interval, first_delay, on_error, }?: RemoteReconnectingOptions): Promise<void>;
|
|
209
|
+
}
|
|
210
|
+
export interface RemoteReconnectingOptions {
|
|
211
|
+
func?: string;
|
|
212
|
+
interval?: number;
|
|
213
|
+
first_delay?: number;
|
|
214
|
+
on_error?(error: Error): void;
|
|
201
215
|
}
|
package/net.browser.js
CHANGED
|
@@ -273,6 +273,7 @@ export class Remote {
|
|
|
273
273
|
handlers = new Map();
|
|
274
274
|
/** `false` 打印所有交互的 rpc messages */
|
|
275
275
|
print = false;
|
|
276
|
+
reconnecting = false;
|
|
276
277
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
277
278
|
static parse(buffer) {
|
|
278
279
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -452,5 +453,38 @@ export class Remote {
|
|
|
452
453
|
}
|
|
453
454
|
});
|
|
454
455
|
}
|
|
456
|
+
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
457
|
+
通常在 on_error 中和首次启动时调用
|
|
458
|
+
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
459
|
+
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
460
|
+
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
461
|
+
- on_error?: 接收每次尝试连接的错误 */
|
|
462
|
+
async start_reconnecting({ func, interval = 1000 * 10, first_delay = 0, on_error, } = {}) {
|
|
463
|
+
assert(this.url);
|
|
464
|
+
if (this.reconnecting)
|
|
465
|
+
return;
|
|
466
|
+
this.reconnecting = true;
|
|
467
|
+
if (first_delay)
|
|
468
|
+
await delay(first_delay);
|
|
469
|
+
for (;;) {
|
|
470
|
+
if (this.lwebsocket.resource?.readyState !== WebSocket.OPEN)
|
|
471
|
+
try {
|
|
472
|
+
if (func)
|
|
473
|
+
await this.call(func);
|
|
474
|
+
else
|
|
475
|
+
await this.connect();
|
|
476
|
+
this.reconnecting = false;
|
|
477
|
+
break;
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
on_error?.(error);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
this.reconnecting = false;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
await delay(interval);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
455
489
|
}
|
|
456
490
|
//# sourceMappingURL=net.browser.js.map
|
package/net.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import { FormData, Headers,
|
|
3
|
-
import { WebSocket,
|
|
4
|
-
import { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
|
|
2
|
+
import type { FormData, Headers, Response, RequestInit } from 'undici';
|
|
3
|
+
import type { WebSocket, CloseEvent, ErrorEvent } from 'ws';
|
|
4
|
+
import type { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
|
|
5
5
|
declare module 'tough-cookie' {
|
|
6
6
|
interface MemoryCookieStore {
|
|
7
7
|
idx: Record<string, any>;
|
|
@@ -10,14 +10,19 @@ declare module 'tough-cookie' {
|
|
|
10
10
|
import './prototype.js';
|
|
11
11
|
import type { Encoding } from './file.js';
|
|
12
12
|
import { inspect, Lock } from './utils.js';
|
|
13
|
+
export type { WebSocket, Headers };
|
|
14
|
+
export declare const WebSocketConnecting = 0;
|
|
15
|
+
export declare const WebSocketOpen = 1;
|
|
16
|
+
export declare const WebSocketClosing = 2;
|
|
17
|
+
export declare const WebSocketClosed = 3;
|
|
13
18
|
export declare enum MyProxy {
|
|
14
19
|
socks5 = "http://localhost:10080",
|
|
15
20
|
whistle = "http://localhost:8899"
|
|
16
21
|
}
|
|
17
|
-
export { Headers };
|
|
18
22
|
export declare const cookies: {
|
|
19
23
|
store: MemoryCookieStore;
|
|
20
24
|
jar: CookieJar;
|
|
25
|
+
init(): Promise<void>;
|
|
21
26
|
get(domain_or_url: string, str?: boolean): Cookie[] | Promise<Cookie[] | string>;
|
|
22
27
|
};
|
|
23
28
|
export { Cookie };
|
|
@@ -100,7 +105,6 @@ export declare function rpc(func: string, args?: any[], { url, async: _async, ig
|
|
|
100
105
|
async?: boolean;
|
|
101
106
|
ignore?: boolean;
|
|
102
107
|
}): Promise<any>;
|
|
103
|
-
export { WebSocket };
|
|
104
108
|
export declare class WebSocketConnectionError extends Error {
|
|
105
109
|
name: string;
|
|
106
110
|
websocket: WebSocket;
|
|
@@ -211,6 +215,7 @@ export declare class Remote {
|
|
|
211
215
|
handlers: Map<number, MessageHandler>;
|
|
212
216
|
/** `false` 打印所有交互的 rpc messages */
|
|
213
217
|
print: boolean;
|
|
218
|
+
reconnecting: boolean;
|
|
214
219
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
215
220
|
static parse<TData extends any[] = any[]>(buffer: Uint8Array): Message<TData>;
|
|
216
221
|
static pack({ id, func, data, done, error }: Message): Uint8Array;
|
|
@@ -246,9 +251,28 @@ export declare class Remote {
|
|
|
246
251
|
作为 websocket 连接发起方,不需要传入 websocket
|
|
247
252
|
作为 websocket 连接接收方,必传 websocket 参数 */
|
|
248
253
|
call<TReturn extends any[] = any[]>(func: string, args?: any[], websocket?: WebSocket): Promise<TReturn>;
|
|
254
|
+
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
255
|
+
通常在 on_error 中和首次启动时调用
|
|
256
|
+
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
257
|
+
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
258
|
+
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
259
|
+
- on_error?: 接收每次尝试连接的错误 */
|
|
260
|
+
start_reconnecting({ func, interval, first_delay, on_error, }?: RemoteReconnectingOptions): Promise<void>;
|
|
249
261
|
}
|
|
250
|
-
/**
|
|
251
|
-
export
|
|
262
|
+
/** 为连接到 server 的 client 创建 RemoteClient,以便后续调用 client 中方法 */
|
|
263
|
+
export declare class RemoteClient {
|
|
252
264
|
remote: Remote;
|
|
253
265
|
websocket: WebSocket;
|
|
266
|
+
constructor(remote: Remote, websocket: WebSocket);
|
|
267
|
+
/** 调用 client 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
|
|
268
|
+
call<TReturn extends any[] = any[]>(func: string, args?: any[]): Promise<TReturn>;
|
|
269
|
+
/** 发送 message 到 client
|
|
270
|
+
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
271
|
+
send(message: Message): Promise<void>;
|
|
272
|
+
}
|
|
273
|
+
export interface RemoteReconnectingOptions {
|
|
274
|
+
func?: string;
|
|
275
|
+
interval?: number;
|
|
276
|
+
first_delay?: number;
|
|
277
|
+
on_error?(error: Error): void;
|
|
254
278
|
}
|
package/net.js
CHANGED
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import { fetch, ProxyAgent, FormData, Headers } from 'undici';
|
|
2
|
-
import { WebSocket } from 'ws';
|
|
3
|
-
import { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';
|
|
4
|
-
import make_fetch_cookie from 'fetch-cookie';
|
|
5
1
|
import { t } from './i18n/instance.js';
|
|
6
2
|
import './prototype.js';
|
|
7
3
|
import { inspect, concat, assert, genid, delay, Lock } from './utils.js';
|
|
4
|
+
export const WebSocketConnecting = 0;
|
|
5
|
+
export const WebSocketOpen = 1;
|
|
6
|
+
export const WebSocketClosing = 2;
|
|
7
|
+
export const WebSocketClosed = 3;
|
|
8
8
|
export var MyProxy;
|
|
9
9
|
(function (MyProxy) {
|
|
10
10
|
MyProxy["socks5"] = "http://localhost:10080";
|
|
11
11
|
MyProxy["whistle"] = "http://localhost:8899";
|
|
12
12
|
})(MyProxy || (MyProxy = {}));
|
|
13
|
-
export { Headers };
|
|
14
13
|
// ------------------------------------ fetch, request
|
|
15
|
-
let cookie_store = new MemoryCookieStore();
|
|
16
14
|
export const cookies = {
|
|
17
|
-
store:
|
|
18
|
-
jar:
|
|
15
|
+
store: null,
|
|
16
|
+
jar: null,
|
|
17
|
+
async init() {
|
|
18
|
+
if (this.jar)
|
|
19
|
+
return;
|
|
20
|
+
const { MemoryCookieStore, CookieJar } = await import('tough-cookie');
|
|
21
|
+
this.jar = new CookieJar(this.store = new MemoryCookieStore());
|
|
22
|
+
},
|
|
19
23
|
get(domain_or_url, str = false) {
|
|
20
24
|
if (domain_or_url.startsWith('http'))
|
|
21
25
|
if (str)
|
|
@@ -31,7 +35,6 @@ export const cookies = {
|
|
|
31
35
|
return cookies;
|
|
32
36
|
},
|
|
33
37
|
};
|
|
34
|
-
export { Cookie };
|
|
35
38
|
/** 对于 request() 函数来说无意义的 headers,会自动过滤掉 */
|
|
36
39
|
const drop_request_headers = new Set([
|
|
37
40
|
// : 开头的 key
|
|
@@ -45,8 +48,12 @@ const drop_request_headers = new Set([
|
|
|
45
48
|
'upgrade',
|
|
46
49
|
]);
|
|
47
50
|
let proxy_agents = {};
|
|
48
|
-
|
|
51
|
+
let fetch_cookie;
|
|
49
52
|
async function fetch_retry(url, options, timeout, retries = 0, count = 0) {
|
|
53
|
+
const { fetch } = await import('undici');
|
|
54
|
+
const { default: make_fetch_cookie } = await import('fetch-cookie');
|
|
55
|
+
await cookies.init();
|
|
56
|
+
fetch_cookie ||= make_fetch_cookie(fetch, cookies.jar, false);
|
|
50
57
|
try {
|
|
51
58
|
options.signal = AbortSignal.timeout(timeout);
|
|
52
59
|
return await fetch_cookie(url, options);
|
|
@@ -64,6 +71,7 @@ async function fetch_retry(url, options, timeout, retries = 0, count = 0) {
|
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
export async function request(url, { method, queries, headers: _headers, body, type = 'application/json', proxy, encoding, retries, timeout = 5 * 1000, auth, cookies: _cookies, raw = false, } = {}) {
|
|
74
|
+
const { Headers, ProxyAgent, FormData } = await import('undici');
|
|
67
75
|
url = new URL(url);
|
|
68
76
|
if (queries)
|
|
69
77
|
for (const key in queries) {
|
|
@@ -278,7 +286,6 @@ export async function rpc(func, args, { url = 'http://localhost:8421/api/rpc', a
|
|
|
278
286
|
}
|
|
279
287
|
let decoder = new TextDecoder();
|
|
280
288
|
let encoder = new TextEncoder();
|
|
281
|
-
export { WebSocket };
|
|
282
289
|
export class WebSocketConnectionError extends Error {
|
|
283
290
|
name = 'WebSocketConnectionError';
|
|
284
291
|
websocket;
|
|
@@ -331,6 +338,7 @@ export class WebSocketConnectionError extends Error {
|
|
|
331
338
|
https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes */
|
|
332
339
|
export async function connect_websocket(url, { protocols, max_payload = 2 ** 33, // 8 GB
|
|
333
340
|
on_message, on_error, on_close }) {
|
|
341
|
+
const { WebSocket } = await import('ws');
|
|
334
342
|
let websocket = new WebSocket(url, protocols, {
|
|
335
343
|
maxPayload: max_payload,
|
|
336
344
|
skipUTF8Validation: true
|
|
@@ -426,6 +434,7 @@ export class Remote {
|
|
|
426
434
|
handlers = new Map();
|
|
427
435
|
/** `false` 打印所有交互的 rpc messages */
|
|
428
436
|
print = false;
|
|
437
|
+
reconnecting = false;
|
|
429
438
|
/** 使用 Uint8Array 作为参数更灵活 https://stackoverflow.com/a/74505197/7609214 */
|
|
430
439
|
static parse(buffer) {
|
|
431
440
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
@@ -500,7 +509,7 @@ export class Remote {
|
|
|
500
509
|
作为 websocket 连接接收方,需要传入使用的 websocket 连接,确保这个这个连接的状态 */
|
|
501
510
|
async connect(websocket) {
|
|
502
511
|
if (this.initiator)
|
|
503
|
-
if (this.lwebsocket.resource?.readyState ===
|
|
512
|
+
if (this.lwebsocket.resource?.readyState === WebSocketOpen)
|
|
504
513
|
return;
|
|
505
514
|
else if (!this.url)
|
|
506
515
|
throw new Error(t('创建 Remote 时传入的 websocket 连接已断开'));
|
|
@@ -510,7 +519,7 @@ export class Remote {
|
|
|
510
519
|
return this.lwebsocket.request(async (websocket) => {
|
|
511
520
|
// 保存的 rpc 状态在 this.handlers, 与 websocket 无关,因此即使断开重连也不影响 rpc 的运行,即
|
|
512
521
|
// 底层连接断开后自动重连对上层应该是无感知的,除非再次连接时失败
|
|
513
|
-
if (websocket?.readyState ===
|
|
522
|
+
if (websocket?.readyState === WebSocketOpen)
|
|
514
523
|
return;
|
|
515
524
|
else // 重连
|
|
516
525
|
this.lwebsocket.resource = await connect_websocket(this.url, {
|
|
@@ -520,7 +529,7 @@ export class Remote {
|
|
|
520
529
|
on_error: this.on_error.bind(this)
|
|
521
530
|
});
|
|
522
531
|
});
|
|
523
|
-
else if (websocket.readyState !==
|
|
532
|
+
else if (websocket.readyState !== WebSocketOpen)
|
|
524
533
|
throw new Error(t('传入的 websocket 连接已断开'));
|
|
525
534
|
}
|
|
526
535
|
/** 作为 websocket 连接发起方手动关闭到对端的 websocket 连接 */
|
|
@@ -576,7 +585,7 @@ export class Remote {
|
|
|
576
585
|
}
|
|
577
586
|
catch (error) {
|
|
578
587
|
// handler 出错并不意味着 rpc 一定会结束,可能 error 是运行中的正常数据,所以不能清理 handler
|
|
579
|
-
if (websocket.readyState ===
|
|
588
|
+
if (websocket.readyState === WebSocketOpen &&
|
|
580
589
|
!message.error // 防止无限循环往对方发送 error, 只有在对方无错误时才可以发送
|
|
581
590
|
)
|
|
582
591
|
await this.send({ id, error, /* 不能设置 done 清理对面 handler, 理由同上 */ }, websocket);
|
|
@@ -605,5 +614,56 @@ export class Remote {
|
|
|
605
614
|
}
|
|
606
615
|
});
|
|
607
616
|
}
|
|
617
|
+
/** 是幂等的,在未连接,或 (on_error) 检测到连接断开后以固定间隔尝试 remote.call(func) 重连
|
|
618
|
+
通常在 on_error 中和首次启动时调用
|
|
619
|
+
- func?: 连接后调用的函数,通常为某个 register 函数,将自身注册到 server
|
|
620
|
+
- duration?: `1000 * 30` 首次重连失败后的后续尝试间隔 (单位: ms)
|
|
621
|
+
- first_delay?: `0` 调用函数后是否立即连接,还是在 first_delay 后重连 (单位: ms)
|
|
622
|
+
- on_error?: 接收每次尝试连接的错误 */
|
|
623
|
+
async start_reconnecting({ func, interval = 1000 * 10, first_delay = 0, on_error, } = {}) {
|
|
624
|
+
assert(this.url);
|
|
625
|
+
if (this.reconnecting)
|
|
626
|
+
return;
|
|
627
|
+
this.reconnecting = true;
|
|
628
|
+
if (first_delay)
|
|
629
|
+
await delay(first_delay);
|
|
630
|
+
for (;;) {
|
|
631
|
+
if (this.lwebsocket.resource?.readyState !== WebSocketOpen)
|
|
632
|
+
try {
|
|
633
|
+
if (func)
|
|
634
|
+
await this.call(func);
|
|
635
|
+
else
|
|
636
|
+
await this.connect();
|
|
637
|
+
this.reconnecting = false;
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
on_error?.(error);
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
this.reconnecting = false;
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
await delay(interval);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/** 为连接到 server 的 client 创建 RemoteClient,以便后续调用 client 中方法 */
|
|
652
|
+
export class RemoteClient {
|
|
653
|
+
remote;
|
|
654
|
+
websocket;
|
|
655
|
+
constructor(remote, websocket) {
|
|
656
|
+
this.remote = remote;
|
|
657
|
+
this.websocket = websocket;
|
|
658
|
+
}
|
|
659
|
+
/** 调用 client 中的 func, 只适用于最简单的一元 rpc (请求, 响应) */
|
|
660
|
+
async call(func, args) {
|
|
661
|
+
return this.remote.call(func, args, this.websocket);
|
|
662
|
+
}
|
|
663
|
+
/** 发送 message 到 client
|
|
664
|
+
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
665
|
+
async send(message) {
|
|
666
|
+
return this.remote.send(message, this.websocket);
|
|
667
|
+
}
|
|
608
668
|
}
|
|
609
669
|
//# sourceMappingURL=net.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.48",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"fs-extra": "^11.1.1",
|
|
63
63
|
"gulp-sort": "^2.0.0",
|
|
64
64
|
"hash-string": "^1.0.0",
|
|
65
|
-
"i18next": "^23.4.
|
|
65
|
+
"i18next": "^23.4.4",
|
|
66
66
|
"i18next-scanner": "^4.3.0",
|
|
67
67
|
"js-cookie": "^3.0.5",
|
|
68
68
|
"koa": "^2.14.2",
|
|
@@ -72,9 +72,8 @@
|
|
|
72
72
|
"map-stream": "0.0.7",
|
|
73
73
|
"mime-types": "^2.1.35",
|
|
74
74
|
"ora": "^7.0.1",
|
|
75
|
-
"qs": "^6.11.2",
|
|
76
75
|
"react": "^18.2.0",
|
|
77
|
-
"react-i18next": "^13.
|
|
76
|
+
"react-i18next": "^13.1.1",
|
|
78
77
|
"resolve-path": "^1.4.0",
|
|
79
78
|
"strip-ansi": "^7.1.0",
|
|
80
79
|
"through2": "^4.0.2",
|
|
@@ -96,11 +95,10 @@
|
|
|
96
95
|
"@types/js-cookie": "^3.0.3",
|
|
97
96
|
"@types/koa": "^2.13.8",
|
|
98
97
|
"@types/koa-compress": "^4.0.3",
|
|
99
|
-
"@types/lodash": "^4.14.
|
|
98
|
+
"@types/lodash": "^4.14.197",
|
|
100
99
|
"@types/mime-types": "^2.1.1",
|
|
101
|
-
"@types/node": "^20.4.
|
|
102
|
-
"@types/
|
|
103
|
-
"@types/react": "^18.2.18",
|
|
100
|
+
"@types/node": "^20.4.9",
|
|
101
|
+
"@types/react": "^18.2.20",
|
|
104
102
|
"@types/through2": "^2.0.38",
|
|
105
103
|
"@types/tough-cookie": "^4.0.2",
|
|
106
104
|
"@types/vinyl-fs": "^3.0.2",
|
package/repl.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import ts from 'typescript';
|
|
2
|
-
import type { Statement } from 'typescript';
|
|
3
1
|
import type { Context } from 'koa';
|
|
4
2
|
import './prototype.js';
|
|
5
3
|
declare global {
|
|
@@ -9,23 +7,6 @@ declare global {
|
|
|
9
7
|
}
|
|
10
8
|
/** 谨慎使用,webpack 打包后可能会变成 /d:/1/mod/node_modules/xshell/ 这样的编译期路径 */
|
|
11
9
|
export declare const fpd_root: string;
|
|
12
|
-
export declare function set_inspection_limit(limit?: number): void;
|
|
13
|
-
export declare function set_printing_compiled_js(enabled: boolean): void;
|
|
14
|
-
export declare function parse_code(code: string): ts.SourceFile;
|
|
15
|
-
export declare function generate_code(stmts: Statement[]): string;
|
|
16
|
-
export declare function compile_ts({ fp, code, print, save, }?: {
|
|
17
|
-
fp?: string;
|
|
18
|
-
code?: string;
|
|
19
|
-
print?: boolean;
|
|
20
|
-
save?: boolean;
|
|
21
|
-
}): Promise<string>;
|
|
22
|
-
/** tsconfig.compilerOptions */
|
|
23
|
-
export declare let ts_options: any;
|
|
24
|
-
export declare let ts_options_commonjs: any;
|
|
25
|
-
export declare let ts_options_commonjs_repl: any;
|
|
26
|
-
export declare function load_tsconfig(): Promise<void>;
|
|
27
|
-
export declare function eval_ts(code: string): Promise<any>;
|
|
28
|
-
export declare function repl_ts(code: string): Promise<any>;
|
|
29
10
|
export declare function start_repl(): Promise<void>;
|
|
30
11
|
export declare function stop(): Promise<void>;
|
|
31
12
|
export declare function exit(): Promise<void>;
|
package/repl.js
CHANGED
|
@@ -1,255 +1,15 @@
|
|
|
1
|
-
import nvm from 'vm';
|
|
2
1
|
import repl from 'repl';
|
|
3
2
|
import process from 'process';
|
|
4
3
|
import { fileURLToPath } from 'url';
|
|
5
|
-
import ts from 'typescript';
|
|
6
|
-
const { factory, createPrinter, SyntaxKind, NodeFlags, ModifierFlags, isIdentifier, isNamedImports, isImportDeclaration, isAwaitExpression, isExpressionStatement, isVariableStatement, isNamespaceImport, isClassDeclaration, isCallExpression, isReturnStatement, isBinaryExpression, isFunctionDeclaration, } = ts;
|
|
7
4
|
import { path } from './path.js';
|
|
8
5
|
import { t } from './i18n/instance.js';
|
|
9
6
|
import './prototype.js';
|
|
10
|
-
import {
|
|
11
|
-
import { fread, fwrite } from './file.js';
|
|
7
|
+
import { delay, set_inspect_options, delta2str } from './utils.js';
|
|
12
8
|
import { Remote } from './net.js';
|
|
13
9
|
set_inspect_options();
|
|
14
10
|
let server;
|
|
15
|
-
let inspection_limit = 10000;
|
|
16
|
-
let printing_compiled_js = false;
|
|
17
11
|
/** 谨慎使用,webpack 打包后可能会变成 /d:/1/mod/node_modules/xshell/ 这样的编译期路径 */
|
|
18
12
|
export const fpd_root = path.dirname(fileURLToPath(import.meta.url)) + '/';
|
|
19
|
-
export function set_inspection_limit(limit = 10000) {
|
|
20
|
-
if (limit === -1)
|
|
21
|
-
limit = 50 * 10 ** 4;
|
|
22
|
-
inspection_limit = limit;
|
|
23
|
-
}
|
|
24
|
-
export function set_printing_compiled_js(enabled) {
|
|
25
|
-
printing_compiled_js = enabled;
|
|
26
|
-
}
|
|
27
|
-
// ------------------------------------ Code Compilers, Transformers
|
|
28
|
-
function resolve_kind(kind) {
|
|
29
|
-
return kind === SyntaxKind.VariableStatement ? 'VariableStatement' : SyntaxKind[kind];
|
|
30
|
-
}
|
|
31
|
-
function print_ast(nodes, sourceFile) {
|
|
32
|
-
function print_node(node) {
|
|
33
|
-
console.log(`${resolve_kind(node.kind)}: ${node.getText(sourceFile)}`);
|
|
34
|
-
}
|
|
35
|
-
if (Array.isArray(nodes))
|
|
36
|
-
nodes.forEach(node => { print_node(node); });
|
|
37
|
-
else
|
|
38
|
-
print_node(nodes);
|
|
39
|
-
}
|
|
40
|
-
export function parse_code(code) {
|
|
41
|
-
return ts.createSourceFile('repl.ts', code, ts.ScriptTarget.ESNext);
|
|
42
|
-
}
|
|
43
|
-
export function generate_code(stmts) {
|
|
44
|
-
return createPrinter({ omitTrailingSemicolon: true, removeComments: false, newLine: ts.NewLineKind.LineFeed })
|
|
45
|
-
.printFile(factory.updateSourceFile(ts.createSourceFile('output.ts', '', ts.ScriptTarget.ESNext), stmts));
|
|
46
|
-
}
|
|
47
|
-
function trans_import_2_require(import_decl) {
|
|
48
|
-
if (!isImportDeclaration(import_decl))
|
|
49
|
-
return import_decl;
|
|
50
|
-
const { importClause: import_clause, moduleSpecifier: module_specifier } = import_decl;
|
|
51
|
-
// require('module_specifier')
|
|
52
|
-
const require_module_stmt = factory.createCallExpression(factory.createIdentifier('require'), [], [module_specifier]);
|
|
53
|
-
// aaa, bbb as ccc, default as ddd, ...
|
|
54
|
-
// (import_clause.namedBindings as NamedImports).elements
|
|
55
|
-
return [
|
|
56
|
-
// import mod from 'mod'
|
|
57
|
-
// globalThis.mod = __importDefault(require("mod")).default
|
|
58
|
-
...import_clause.name ? [
|
|
59
|
-
factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier('globalThis'), import_clause.name), factory.createPropertyAccessExpression(factory.createCallExpression(factory.createIdentifier('__importDefault'), [], [require_module_stmt]), 'default')))
|
|
60
|
-
] : [],
|
|
61
|
-
// import * as mod from 'mod'
|
|
62
|
-
// globalThis.mod = __importStar(require("mod"))
|
|
63
|
-
...import_clause.namedBindings && isNamespaceImport(import_clause.namedBindings) ? [
|
|
64
|
-
factory.createExpressionStatement(factory.createAssignment(factory.createPropertyAccessExpression(factory.createIdentifier('globalThis'), import_clause.namedBindings.name), factory.createCallExpression(factory.createIdentifier('__importStar'), [], [require_module_stmt])))
|
|
65
|
-
] : [],
|
|
66
|
-
// import { element2 as element2_, element3 as element3_, other, default as mod2 } from 'mod2'
|
|
67
|
-
...import_clause.namedBindings && isNamedImports(import_clause.namedBindings) ? [
|
|
68
|
-
// var { element2: element2_, element3 } = __importDefault(require("mod"))
|
|
69
|
-
factory.createVariableStatement([], [factory.createVariableDeclaration(factory.createObjectBindingPattern(
|
|
70
|
-
// ImportSpecifier[] -> ObjectBindingPattern.BindingElement[]
|
|
71
|
-
import_clause.namedBindings.elements.map(import_specifier => factory.createBindingElement(undefined, import_specifier.propertyName, import_specifier.name, import_specifier.propertyName?.text === 'default' ? require_module_stmt : undefined))), undefined, undefined, require_module_stmt)]),
|
|
72
|
-
// Object.assign(globalThis, { element2_, element3 })
|
|
73
|
-
factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('Object'), 'assign'), [],
|
|
74
|
-
// ImportSpecifier[] -> ObjectBindingPattern.BindingElement[]
|
|
75
|
-
[factory.createIdentifier('globalThis'), factory.createObjectLiteralExpression(import_clause.namedBindings.elements.map(import_specifier => factory.createShorthandPropertyAssignment(import_specifier.name)), false)])),
|
|
76
|
-
factory.createExpressionStatement(import_clause.namedBindings.elements.length === 1 ?
|
|
77
|
-
// (a)
|
|
78
|
-
factory.createParenthesizedExpression(import_clause.namedBindings.elements[0].name)
|
|
79
|
-
: // ({ a: xxx, b: xxx })
|
|
80
|
-
factory.createObjectLiteralExpression(import_clause.namedBindings.elements.map(import_specifier => factory.createShorthandPropertyAssignment(import_specifier.name)), false))
|
|
81
|
-
] : [],
|
|
82
|
-
];
|
|
83
|
-
}
|
|
84
|
-
/** export function foo () { } */
|
|
85
|
-
function trans_export_stmt(stmt) {
|
|
86
|
-
if (ts.isExportAssignment(stmt))
|
|
87
|
-
return factory.createExpressionStatement(stmt.expression);
|
|
88
|
-
function is_export_modifier(modifier) {
|
|
89
|
-
return modifier.flags & ModifierFlags.Export || modifier.flags & ModifierFlags.ExportDefault || modifier.kind === SyntaxKind.ExportKeyword;
|
|
90
|
-
}
|
|
91
|
-
// @ts-ignore
|
|
92
|
-
if (stmt.modifiers?.some(modifier => is_export_modifier(modifier))) {
|
|
93
|
-
// @ts-ignore
|
|
94
|
-
stmt.modifiers = factory.createNodeArray(
|
|
95
|
-
// @ts-ignore
|
|
96
|
-
stmt.modifiers.filter(modifier => !is_export_modifier(modifier)));
|
|
97
|
-
return stmt;
|
|
98
|
-
}
|
|
99
|
-
return stmt;
|
|
100
|
-
}
|
|
101
|
-
/** const a = 123, b = 234, { c, d: e } = obj */
|
|
102
|
-
function trans_variable_decl_2_var(var_decl) {
|
|
103
|
-
if (!isVariableStatement(var_decl))
|
|
104
|
-
return var_decl;
|
|
105
|
-
// @ts-ignore
|
|
106
|
-
var_decl.declarationList.flags = NodeFlags.None;
|
|
107
|
-
return var_decl;
|
|
108
|
-
}
|
|
109
|
-
function trans_class_decl_2_expr(class_decl) {
|
|
110
|
-
if (!isClassDeclaration(class_decl))
|
|
111
|
-
return class_decl;
|
|
112
|
-
// export class C { } 这样的语句已经在 trans_export_stmt 中被去掉了 export modifier
|
|
113
|
-
return factory.createVariableStatement([], factory.createVariableDeclarationList([
|
|
114
|
-
factory.createVariableDeclaration(class_decl.name, undefined, undefined, factory.createClassExpression(undefined, class_decl.name, undefined, class_decl.heritageClauses, class_decl.members))
|
|
115
|
-
]));
|
|
116
|
-
}
|
|
117
|
-
function get_expr_of_stmt(statement) {
|
|
118
|
-
if (isExpressionStatement(statement))
|
|
119
|
-
return statement;
|
|
120
|
-
if (isVariableStatement(statement)) {
|
|
121
|
-
const { declarations } = statement.declarationList;
|
|
122
|
-
return factory.createExpressionStatement(declarations.length === 1 ?
|
|
123
|
-
(() => {
|
|
124
|
-
const { name } = declarations[0];
|
|
125
|
-
if (isIdentifier(name)) // const a = c
|
|
126
|
-
return name;
|
|
127
|
-
else // const { a, b } = c
|
|
128
|
-
return factory.createObjectLiteralExpression(name.elements
|
|
129
|
-
.filter(({ name }) => name && isIdentifier(name))
|
|
130
|
-
.map(({ name }) => factory.createShorthandPropertyAssignment(name)));
|
|
131
|
-
})()
|
|
132
|
-
:
|
|
133
|
-
factory.createObjectLiteralExpression(declarations.map(var_decl => factory.createShorthandPropertyAssignment(var_decl.name)), true));
|
|
134
|
-
}
|
|
135
|
-
if (isFunctionDeclaration(statement))
|
|
136
|
-
return factory.createExpressionStatement(statement.name);
|
|
137
|
-
return statement;
|
|
138
|
-
}
|
|
139
|
-
function trans_return_2_expr(stmt) {
|
|
140
|
-
if (!isReturnStatement(stmt))
|
|
141
|
-
return stmt;
|
|
142
|
-
return factory.createExpressionStatement(stmt.expression);
|
|
143
|
-
}
|
|
144
|
-
function assign_var_decl(var_decl, destination = 'globalThis') {
|
|
145
|
-
if (!isVariableStatement(var_decl))
|
|
146
|
-
return [var_decl];
|
|
147
|
-
// VariableDeclaration[] -> ObjectLiteral[]
|
|
148
|
-
// const a = 123, b = 234, { c, d: e } = obj
|
|
149
|
-
const obj_literal = factory.createObjectLiteralExpression(var_decl.declarationList.declarations.map(declaration => isIdentifier(declaration.name) ?
|
|
150
|
-
[factory.createShorthandPropertyAssignment(declaration.name)]
|
|
151
|
-
: // ObjectBindingPattern
|
|
152
|
-
declaration.name.elements.map((element) => factory.createShorthandPropertyAssignment(element.name))).flat(), true);
|
|
153
|
-
return [
|
|
154
|
-
var_decl,
|
|
155
|
-
factory.createExpressionStatement(factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier('Object'), 'assign'), [], [factory.createIdentifier(destination), obj_literal])),
|
|
156
|
-
factory.createExpressionStatement(obj_literal)
|
|
157
|
-
];
|
|
158
|
-
}
|
|
159
|
-
function return_last_expr(statements) {
|
|
160
|
-
if (!statements.length)
|
|
161
|
-
return statements;
|
|
162
|
-
const last_expr = get_expr_of_stmt(statements.last);
|
|
163
|
-
if (isExpressionStatement(last_expr))
|
|
164
|
-
return [...statements.slice(0, -1), factory.createReturnStatement(last_expr.expression)];
|
|
165
|
-
else
|
|
166
|
-
return statements;
|
|
167
|
-
}
|
|
168
|
-
function wrap_await_stmt(statements, code) {
|
|
169
|
-
function wrap(stmts) {
|
|
170
|
-
return [
|
|
171
|
-
factory.createExpressionStatement(factory.createCallExpression(factory.createFunctionExpression([factory.createModifier(SyntaxKind.AsyncKeyword)], undefined, 'async_wrapper', [], [], undefined, factory.createBlock(return_last_expr(stmts.map(statement => assign_var_decl(statement)).flat()), true)), [], [])),
|
|
172
|
-
];
|
|
173
|
-
}
|
|
174
|
-
if (!code.includes('await'))
|
|
175
|
-
return statements;
|
|
176
|
-
if (code.includes('await') && !code.includes('async '))
|
|
177
|
-
return wrap(statements);
|
|
178
|
-
if (statements.some(stmt => (isExpressionStatement(stmt) && isAwaitExpression(stmt.expression) ||
|
|
179
|
-
isVariableStatement(stmt) && stmt.declarationList.declarations.some(var_decl => var_decl.initializer && isAwaitExpression(var_decl.initializer)) ||
|
|
180
|
-
isExpressionStatement(stmt) && isCallExpression(stmt.expression) && stmt.expression.arguments.some(expr => isAwaitExpression(expr)) ||
|
|
181
|
-
isExpressionStatement(stmt) && isBinaryExpression(stmt.expression) && isAwaitExpression(stmt.expression.right))))
|
|
182
|
-
return wrap(statements);
|
|
183
|
-
return statements;
|
|
184
|
-
}
|
|
185
|
-
export async function compile_ts({ fp, code, print = printing_compiled_js, save = false, } = {}) {
|
|
186
|
-
if (!code && fp)
|
|
187
|
-
code = await fread(fp);
|
|
188
|
-
const source_file = parse_code(code);
|
|
189
|
-
let statements = [...source_file.statements];
|
|
190
|
-
statements = statements.map(trans_import_2_require).flat();
|
|
191
|
-
statements = statements.map(trans_export_stmt);
|
|
192
|
-
statements = statements.map(trans_class_decl_2_expr);
|
|
193
|
-
statements = wrap_await_stmt(statements, code);
|
|
194
|
-
statements = statements.map(trans_variable_decl_2_var);
|
|
195
|
-
statements = statements.map(trans_return_2_expr);
|
|
196
|
-
if (statements.length) {
|
|
197
|
-
const last_stmt = statements[statements.length - 1];
|
|
198
|
-
const last_expr = get_expr_of_stmt(last_stmt);
|
|
199
|
-
if (last_expr !== last_stmt)
|
|
200
|
-
statements = [...statements, last_expr];
|
|
201
|
-
}
|
|
202
|
-
code = generate_code(statements);
|
|
203
|
-
let { diagnostics, outputText: output_text } = ts.transpileModule(code, { compilerOptions: ts_options_commonjs_repl });
|
|
204
|
-
if (diagnostics.length) {
|
|
205
|
-
console.log(diagnostics.join('\n\n\n'));
|
|
206
|
-
log_line();
|
|
207
|
-
}
|
|
208
|
-
if (print) {
|
|
209
|
-
console.log(output_text);
|
|
210
|
-
log_line();
|
|
211
|
-
}
|
|
212
|
-
if (fp && save)
|
|
213
|
-
await fwrite(fp.replace(/\.ts(x?)$/, '.js$1'), output_text);
|
|
214
|
-
return output_text;
|
|
215
|
-
}
|
|
216
|
-
/** tsconfig.compilerOptions */
|
|
217
|
-
export let ts_options;
|
|
218
|
-
export let ts_options_commonjs;
|
|
219
|
-
export let ts_options_commonjs_repl;
|
|
220
|
-
export async function load_tsconfig() {
|
|
221
|
-
const fp = `${fpd_root}tsconfig.json`;
|
|
222
|
-
({ config: { compilerOptions: ts_options } } = ts.parseConfigFileTextToJson(fp, await fread(fp, { print: false })));
|
|
223
|
-
ts_options_commonjs = {
|
|
224
|
-
...ts_options,
|
|
225
|
-
module: 'CommonJS',
|
|
226
|
-
incremental: false,
|
|
227
|
-
// determine CommonJS module require method
|
|
228
|
-
esModuleInterop: true,
|
|
229
|
-
};
|
|
230
|
-
ts_options_commonjs_repl = {
|
|
231
|
-
...ts_options,
|
|
232
|
-
module: 'CommonJS',
|
|
233
|
-
esModuleInterop: true,
|
|
234
|
-
// nvm.runInThisContext doesn't support inline source map
|
|
235
|
-
sourceMap: false,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
let eval_id = 0;
|
|
239
|
-
export async function eval_ts(code) {
|
|
240
|
-
return nvm.runInThisContext(await compile_ts({ code }), `repl/${eval_id++}.ts`);
|
|
241
|
-
}
|
|
242
|
-
// ------------------------------------ repl
|
|
243
|
-
export async function repl_ts(code) {
|
|
244
|
-
console.log(); // 离之前的输出有一个空行
|
|
245
|
-
const timer = new Timer();
|
|
246
|
-
const __ = globalThis.__ = await eval_ts(code);
|
|
247
|
-
timer.stop();
|
|
248
|
-
console.log(inspect(__, { limit: inspection_limit }));
|
|
249
|
-
if (timer.get() >= 500)
|
|
250
|
-
timer.print();
|
|
251
|
-
return __;
|
|
252
|
-
}
|
|
253
13
|
export async function start_repl() {
|
|
254
14
|
// ------------ 加载库
|
|
255
15
|
console.log(t('xshell 开始启动').yellow);
|
|
@@ -269,7 +29,6 @@ export async function start_repl() {
|
|
|
269
29
|
process.title = 'xshell';
|
|
270
30
|
await Promise.all([
|
|
271
31
|
pollute_global(),
|
|
272
|
-
load_tsconfig(),
|
|
273
32
|
(async () => {
|
|
274
33
|
// --- http server
|
|
275
34
|
let { Server } = await import('./server.js');
|
|
@@ -281,9 +40,6 @@ export async function start_repl() {
|
|
|
281
40
|
console.log('echo:', data);
|
|
282
41
|
return [data];
|
|
283
42
|
},
|
|
284
|
-
async repl_ts({ data: [code] }) {
|
|
285
|
-
return [await repl_ts(code)];
|
|
286
|
-
}
|
|
287
43
|
}
|
|
288
44
|
})
|
|
289
45
|
});
|
|
@@ -323,10 +79,6 @@ export async function pollute_global() {
|
|
|
323
79
|
exports: globalThis
|
|
324
80
|
});
|
|
325
81
|
await Promise.all([
|
|
326
|
-
pollute_module_default_export('lodash/omit.js', 'omit'),
|
|
327
|
-
pollute_module_default_export('lodash/sortBy.js', 'sort_by'),
|
|
328
|
-
pollute_module_default_export('lodash/groupBy.js', 'group_by'),
|
|
329
|
-
pollute_module_default_export('qs', 'qs'),
|
|
330
82
|
pollute_module_exports('./path.js'),
|
|
331
83
|
pollute_module_exports('./prototype.js'),
|
|
332
84
|
pollute_module_exports('./utils.js'),
|
package/server.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
/// <reference types="node" resolution-mode="require"/>
|
|
3
3
|
import { type Server as HttpServer } from 'http';
|
|
4
|
-
import { WebSocketServer } from 'ws';
|
|
5
|
-
import { default as Koa,
|
|
6
|
-
import {
|
|
4
|
+
import type { WebSocketServer } from 'ws';
|
|
5
|
+
import type { default as Koa, Context, Next } from 'koa';
|
|
6
|
+
import type { UserAgentContext } from 'koa-useragent';
|
|
7
7
|
declare module 'koa' {
|
|
8
8
|
interface Request {
|
|
9
9
|
/** 经过 decodeURIComponent 后,在路径重写之前的路径 */
|
package/server.js
CHANGED
|
@@ -3,16 +3,7 @@ import zlib from 'zlib';
|
|
|
3
3
|
import { createReadStream } from 'fs';
|
|
4
4
|
import { Readable } from 'stream';
|
|
5
5
|
import util from 'util';
|
|
6
|
-
|
|
7
|
-
import qs from 'qs';
|
|
8
|
-
import resolve_safely from 'resolve-path';
|
|
9
|
-
import { WebSocketServer } from 'ws';
|
|
10
|
-
import { contentType } from 'mime-types';
|
|
11
|
-
// --- koa & koa middleware
|
|
12
|
-
import { default as Koa } from 'koa';
|
|
13
|
-
import KoaCors from '@koa/cors';
|
|
14
|
-
import KoaCompress from 'koa-compress';
|
|
15
|
-
import { userAgent as KoaUserAgent } from 'koa-useragent';
|
|
6
|
+
import querystring from 'querystring';
|
|
16
7
|
// --- my libs
|
|
17
8
|
import { t } from './i18n/instance.js';
|
|
18
9
|
import { request as _request } from './net.js';
|
|
@@ -45,6 +36,11 @@ export class Server {
|
|
|
45
36
|
}
|
|
46
37
|
/** start http server and listen */
|
|
47
38
|
async start() {
|
|
39
|
+
const { default: Koa } = await import('koa');
|
|
40
|
+
const { default: KoaCors } = await import('@koa/cors');
|
|
41
|
+
const { default: KoaCompress } = await import('koa-compress');
|
|
42
|
+
const { userAgent: KoaUserAgent } = await import('koa-useragent');
|
|
43
|
+
const { WebSocketServer } = await import('ws');
|
|
48
44
|
// --- init koa app
|
|
49
45
|
let app = new Koa();
|
|
50
46
|
app.on('error', (error, ctx) => {
|
|
@@ -138,7 +134,7 @@ export class Server {
|
|
|
138
134
|
if (ctx.is('application/json') || ctx.is('text/plain'))
|
|
139
135
|
request.body = JSON.parse(req.body.toString());
|
|
140
136
|
else if (ctx.is('application/x-www-form-urlencoded'))
|
|
141
|
-
request.body =
|
|
137
|
+
request.body = querystring.parse(req.body.toString());
|
|
142
138
|
else if (ctx.is('multipart/form-data'))
|
|
143
139
|
throw new Error('multipart/form-data is not supported');
|
|
144
140
|
else
|
|
@@ -315,6 +311,7 @@ export class Server {
|
|
|
315
311
|
fp = fp.slice(root.length);
|
|
316
312
|
if (fp.startsWith('/'))
|
|
317
313
|
fp = fp.slice(1);
|
|
314
|
+
const { default: resolve_safely } = await import('resolve-path');
|
|
318
315
|
try {
|
|
319
316
|
fp = resolve_safely(root, fp).fp;
|
|
320
317
|
}
|
|
@@ -347,8 +344,9 @@ export class Server {
|
|
|
347
344
|
if (download)
|
|
348
345
|
response.set('content-disposition', `attachment; filename="${encodeURIComponent(fp.fname)}"`);
|
|
349
346
|
if (!response.get('content-type')) {
|
|
347
|
+
const { contentType: get_content_type } = await import('mime-types');
|
|
350
348
|
const fext = fp.fext;
|
|
351
|
-
response.set('content-type', (this.js_exts.has(fext) ? 'text/javascript; chatset=utf-8' :
|
|
349
|
+
response.set('content-type', (this.js_exts.has(fext) ? 'text/javascript; chatset=utf-8' : get_content_type(fp.fext))
|
|
352
350
|
|| 'application/octet-stream');
|
|
353
351
|
}
|
|
354
352
|
if (request.fresh) {
|
package/utils.browser.js
CHANGED
|
@@ -138,7 +138,7 @@ export function delta2str(delta) {
|
|
|
138
138
|
return '0 ms';
|
|
139
139
|
// [1, 1000) ms
|
|
140
140
|
if (delta < 1000)
|
|
141
|
-
return `${delta} ms`;
|
|
141
|
+
return `${delta.toFixed(0)} ms`;
|
|
142
142
|
// 1.123 s
|
|
143
143
|
if (1000 <= delta && delta < 1000 * 60)
|
|
144
144
|
return `${(delta / 1000).toFixed(1)} s`;
|
package/utils.js
CHANGED
|
@@ -101,7 +101,7 @@ export function delta2str(delta) {
|
|
|
101
101
|
return '0 ms';
|
|
102
102
|
// [1, 1000) ms
|
|
103
103
|
if (delta < 1000)
|
|
104
|
-
return `${delta} ms`;
|
|
104
|
+
return `${delta.toFixed(0)} ms`;
|
|
105
105
|
// 1.123 s
|
|
106
106
|
if (1000 <= delta && delta < 1000 * 60)
|
|
107
107
|
return `${(delta / 1000).toFixed(1)} s`;
|