xshell 1.1.15 → 1.1.16
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/development.d.ts +6 -0
- package/development.js +35 -0
- package/net.browser.js +2 -2
- package/net.js +3 -3
- package/package.json +1 -1
- package/process.d.ts +61 -19
- package/process.js +106 -30
- package/server.d.ts +1 -1
- package/server.js +38 -38
- package/utils.browser.d.ts +5 -3
- package/utils.browser.js +5 -3
- package/utils.d.ts +7 -5
- package/utils.js +7 -5
package/development.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** 监听终端按键 (输入),并调用 key_processor 处理
|
|
2
|
+
- on_key: 按键处理函数
|
|
3
|
+
- on_exit?: ctrl + c 会退出进程,可加入退出前自定义处理逻辑 */
|
|
4
|
+
export declare function process_stdin(on_key: (key: string) => void | Promise<void>, on_exit?: () => void | Promise<void>): void;
|
|
5
|
+
/** 自动配置项目 vscode 设置 */
|
|
6
|
+
export declare function setup_vscode_settings(fpd_root: string): Promise<void>;
|
package/development.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import process from 'process';
|
|
2
|
+
import { fcopy, fexists, ramdisk } from "./file.js";
|
|
3
|
+
import { noprint } from "./process.js";
|
|
4
|
+
/** 监听终端按键 (输入),并调用 key_processor 处理
|
|
5
|
+
- on_key: 按键处理函数
|
|
6
|
+
- on_exit?: ctrl + c 会退出进程,可加入退出前自定义处理逻辑 */
|
|
7
|
+
export function process_stdin(on_key, on_exit) {
|
|
8
|
+
// https://stackoverflow.com/a/12506613/7609214
|
|
9
|
+
let { stdin } = process;
|
|
10
|
+
if (stdin.isTTY) {
|
|
11
|
+
stdin.setRawMode(true);
|
|
12
|
+
stdin.resume();
|
|
13
|
+
}
|
|
14
|
+
stdin.setEncoding('utf-8');
|
|
15
|
+
// on any data into stdin
|
|
16
|
+
stdin.on('data', async (key) => {
|
|
17
|
+
// ctrl-c ( end of text )
|
|
18
|
+
if (key === '\u0003') {
|
|
19
|
+
await on_exit?.();
|
|
20
|
+
process.exit();
|
|
21
|
+
}
|
|
22
|
+
// write the key to stdout all normal like
|
|
23
|
+
console.log(key);
|
|
24
|
+
await on_key(key);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** 自动配置项目 vscode 设置 */
|
|
28
|
+
export async function setup_vscode_settings(fpd_root) {
|
|
29
|
+
if (!ramdisk) {
|
|
30
|
+
const fp_settings = `${fpd_root}.vscode/settings.json`;
|
|
31
|
+
if (!fexists(fp_settings, noprint))
|
|
32
|
+
await fcopy(`${fp_settings.strip_end('json')}template.json`, fp_settings);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=development.js.map
|
package/net.browser.js
CHANGED
|
@@ -384,7 +384,7 @@ export class Remote {
|
|
|
384
384
|
this.reconnecting = false;
|
|
385
385
|
if (!this.disconnected)
|
|
386
386
|
try {
|
|
387
|
-
await timeout(3000,
|
|
387
|
+
await timeout(3000, this.connect(), undefined, this.print);
|
|
388
388
|
this.first_error = true;
|
|
389
389
|
}
|
|
390
390
|
catch (error) {
|
|
@@ -451,7 +451,7 @@ export class Remote {
|
|
|
451
451
|
break;
|
|
452
452
|
if (!this.reconnecting)
|
|
453
453
|
try {
|
|
454
|
-
await timeout(1000 * 2,
|
|
454
|
+
await timeout(1000 * 2, this.call('echo'), undefined, this.print);
|
|
455
455
|
}
|
|
456
456
|
catch (error) {
|
|
457
457
|
if (this.print)
|
package/net.js
CHANGED
|
@@ -46,7 +46,7 @@ async function request_retry(url, options, _timeout, retries = 0, count = 0, pri
|
|
|
46
46
|
// 设置给 undici 设置 timeout, signal 不一定管用,还是得自己兜底
|
|
47
47
|
options.signal = AbortSignal.timeout(_timeout);
|
|
48
48
|
return await timeout(_timeout + 300, // 为 undici 兜底
|
|
49
|
-
|
|
49
|
+
undici.request(url, options), undefined, print.timeout && count >= retries // 只打印最后一次超时的错误,避免太多冗余输出
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
else
|
|
@@ -569,7 +569,7 @@ export class Remote {
|
|
|
569
569
|
this.reconnecting = false;
|
|
570
570
|
if (!this.disconnected)
|
|
571
571
|
try {
|
|
572
|
-
await timeout(3000,
|
|
572
|
+
await timeout(3000, this.connect(), undefined, this.print);
|
|
573
573
|
this.first_error = true;
|
|
574
574
|
}
|
|
575
575
|
catch (error) {
|
|
@@ -636,7 +636,7 @@ export class Remote {
|
|
|
636
636
|
break;
|
|
637
637
|
if (!this.reconnecting)
|
|
638
638
|
try {
|
|
639
|
-
await timeout(1000 * 2,
|
|
639
|
+
await timeout(1000 * 2, this.call('echo'), undefined, this.print);
|
|
640
640
|
}
|
|
641
641
|
catch (error) {
|
|
642
642
|
if (this.print)
|
package/package.json
CHANGED
package/process.d.ts
CHANGED
|
@@ -49,6 +49,8 @@ interface BaseOptions {
|
|
|
49
49
|
|
|
50
50
|
具体有什么用还不清楚 */
|
|
51
51
|
window?: boolean;
|
|
52
|
+
/** 由创建进程的调用者设置的,用于区分、识别、记忆的可选名称 */
|
|
53
|
+
title?: string;
|
|
52
54
|
}
|
|
53
55
|
export interface StartOptions extends BaseOptions {
|
|
54
56
|
/** `true` 是否打印启动命令行 */
|
|
@@ -60,7 +62,25 @@ export interface StartOptions extends BaseOptions {
|
|
|
60
62
|
/** 使用文件作为标准错误 */
|
|
61
63
|
fp_stderr?: string;
|
|
62
64
|
}
|
|
63
|
-
|
|
65
|
+
export declare function get_command(exe: string, args?: string[]): string;
|
|
66
|
+
export interface SubProcess<TOutput extends string | Buffer = string> extends ChildProcess {
|
|
67
|
+
/** 由创建进程的调用者设置的,用于区分、识别、记忆的可选名称 */
|
|
68
|
+
title?: string;
|
|
69
|
+
/** 启动的 exe 路径,等于传入 call, start 的 exe */
|
|
70
|
+
exe: string;
|
|
71
|
+
/** 启动参数, 无参数时确保为 `[ ]` */
|
|
72
|
+
args: string[];
|
|
73
|
+
/** 启动命令 */
|
|
74
|
+
command: string;
|
|
75
|
+
/** 调用 call 的 Promise 结果,call, launch 都有,start 创建的进程没有 */
|
|
76
|
+
presult: Promise<CallResult<TOutput>>;
|
|
77
|
+
/** 是否还在运行 */
|
|
78
|
+
get running(): boolean;
|
|
79
|
+
/** 是否已结束,等价于 !running */
|
|
80
|
+
get finished(): boolean;
|
|
81
|
+
}
|
|
82
|
+
/** 启动独立 (detached) 的进程,不受当前 node.js 进程退出的影响,重定向 stdio 到文件或直接忽略
|
|
83
|
+
使用 windowsHide 选项避免创建命令行窗口
|
|
64
84
|
- exe: .exe 路径或文件名 (建议使用完整路径,跳过 path 搜索,性能更高)
|
|
65
85
|
- args?: `[ ]` 参数列表
|
|
66
86
|
- options?:
|
|
@@ -72,7 +92,7 @@ export interface StartOptions extends BaseOptions {
|
|
|
72
92
|
- fp_stdin?: 使用文件作为标准输入,设置 stdio 中对应的值
|
|
73
93
|
- fp_stdout?: 使用文件作为标准输出,设置 stdio 中对应的值
|
|
74
94
|
- fp_stderr?: 使用文件作为标准错误,设置 stdio 中对应的值 */
|
|
75
|
-
export declare function start(exe: string, args?: string[], options?: StartOptions): Promise<
|
|
95
|
+
export declare function start(exe: string, args?: string[], options?: StartOptions): Promise<SubProcess>;
|
|
76
96
|
export interface CallOptions extends BaseOptions {
|
|
77
97
|
/** `true` print 选项,支持设置细项 */
|
|
78
98
|
print?: boolean | FullPrintOptions;
|
|
@@ -90,7 +110,7 @@ export interface CallOptions extends BaseOptions {
|
|
|
90
110
|
stderr?: (chunk: string) => void;
|
|
91
111
|
};
|
|
92
112
|
/** 可以传入回调函数及时获取通过 start 创建的子进程,便于执行 kill、获得 pid 等操作 */
|
|
93
|
-
on_child?: (child:
|
|
113
|
+
on_child?: (child: SubProcess) => void;
|
|
94
114
|
}
|
|
95
115
|
export interface CallResult<TOutput extends string | Buffer = string> {
|
|
96
116
|
pid: number;
|
|
@@ -100,7 +120,7 @@ export interface CallResult<TOutput extends string | Buffer = string> {
|
|
|
100
120
|
signal: NodeJS.Signals | null;
|
|
101
121
|
command: string;
|
|
102
122
|
message: string;
|
|
103
|
-
child:
|
|
123
|
+
child: SubProcess<TOutput>;
|
|
104
124
|
print: FullPrintOptions;
|
|
105
125
|
}
|
|
106
126
|
export interface CallError<TOutput extends string | Buffer = string> extends Error {
|
|
@@ -110,7 +130,7 @@ export interface CallError<TOutput extends string | Buffer = string> extends Err
|
|
|
110
130
|
code: number;
|
|
111
131
|
signal: NodeJS.Signals;
|
|
112
132
|
command: string;
|
|
113
|
-
child:
|
|
133
|
+
child: SubProcess<TOutput>;
|
|
114
134
|
print: FullPrintOptions;
|
|
115
135
|
}
|
|
116
136
|
export declare class CallError<TOutput extends string | Buffer = string> extends Error {
|
|
@@ -118,7 +138,7 @@ export declare class CallError<TOutput extends string | Buffer = string> extends
|
|
|
118
138
|
constructor({ message, pid, stdout, stderr, code, signal, command, child, print }: CallResult<TOutput>);
|
|
119
139
|
[inspect.custom](depth: number, options: InspectOptions, inspect: Function): string;
|
|
120
140
|
}
|
|
121
|
-
/**
|
|
141
|
+
/** 调用 exe 启动子进程,等待并获取返回结果,错误时抛出 CallError
|
|
122
142
|
- exe: .exe 路径或文件名 (建议使用路径,跳过 path 搜索,性能更高)
|
|
123
143
|
- args: `[ ]` 参数列表
|
|
124
144
|
- options?:
|
|
@@ -135,14 +155,26 @@ export declare function call(exe: string, args: string[], options: CallOptions &
|
|
|
135
155
|
encoding: 'binary';
|
|
136
156
|
}): Promise<CallResult<Buffer>>;
|
|
137
157
|
export declare function call(exe: string, args?: string[], options?: CallOptions): Promise<CallResult<string>>;
|
|
138
|
-
|
|
158
|
+
/** 调用 exe 启动子进程(通常是长期在后台运行的,或者可交互的,需要能控制)
|
|
159
|
+
返回 {@link SubProcess} 对象 (继承自 ChildProcess),可以做 .kill() 等操作
|
|
160
|
+
其中有 presult 属性,是调用 call 的 Promise 结果
|
|
161
|
+
- exe: .exe 路径或文件名 (建议使用路径,跳过 path 搜索,性能更高)
|
|
162
|
+
- args: `[ ]` 参数列表
|
|
163
|
+
- options?: 见 {@link CallOptions} */
|
|
164
|
+
export declare function launch(exe: string, args: string[], options: CallOptions & {
|
|
165
|
+
encoding: 'binary';
|
|
166
|
+
}): Promise<SubProcess<Buffer>>;
|
|
167
|
+
export declare function launch(exe: string, args?: string[], options?: CallOptions): Promise<SubProcess<string>>;
|
|
168
|
+
export interface NodeJsOptions {
|
|
139
169
|
inspect?: number | true;
|
|
140
170
|
break?: boolean;
|
|
141
171
|
}
|
|
172
|
+
export interface CallNodeJsOptions extends CallOptions, NodeJsOptions {
|
|
173
|
+
}
|
|
142
174
|
/** 调用 node <js> 并等待结果
|
|
143
175
|
- js: .js 路径 (相对路径根据 cwd 解析)
|
|
144
176
|
- args?: `[ ]` 参数列表
|
|
145
|
-
- options?:
|
|
177
|
+
- options?: {@link CallNodeJsOptions} 继承自 {@link CallOptions} 和 {@link NodeJsOptions}
|
|
146
178
|
- cwd?: `'T:/'`
|
|
147
179
|
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量
|
|
148
180
|
- encoding?: `'utf-8'` 子进程输出编码
|
|
@@ -151,19 +183,29 @@ export interface CallNodeJsOptions extends CallOptions {
|
|
|
151
183
|
- throw_code?: `true` code 不为 0 时是否抛出异常
|
|
152
184
|
- inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
|
|
153
185
|
- break?: break at first line */
|
|
154
|
-
export declare function call_nodejs(js: string, args?: string[],
|
|
155
|
-
export interface StartNodeJsOptions extends StartOptions {
|
|
156
|
-
inspect?: number | true;
|
|
157
|
-
break?: boolean;
|
|
186
|
+
export declare function call_nodejs(js: string, args?: string[], options?: CallNodeJsOptions): Promise<CallResult<string>>;
|
|
187
|
+
export interface StartNodeJsOptions extends StartOptions, NodeJsOptions {
|
|
158
188
|
}
|
|
159
|
-
/**
|
|
189
|
+
/** 启动独立的 node.js 进程 (detached),执行某个 js 文件
|
|
160
190
|
- js: .js 路径 (相对路径根据 cwd 解析)
|
|
161
191
|
- args: `[]` 参数列表
|
|
162
|
-
- options
|
|
163
|
-
- cwd?: `'T:/'`
|
|
164
|
-
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量
|
|
165
|
-
- print?: `true` print 选项,支持设置细项
|
|
192
|
+
- options?: {@link StartNodeJsOptions} 继承自 {@link StartOptions} 和 {@link NodeJsOptions}
|
|
166
193
|
- inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
|
|
167
|
-
- break?:
|
|
168
|
-
export declare function start_nodejs(js: string, args?: string[],
|
|
194
|
+
- break?: 在第一行停止 */
|
|
195
|
+
export declare function start_nodejs(js: string, args?: string[], options?: StartNodeJsOptions): Promise<SubProcess<string>>;
|
|
196
|
+
/** 启动子 node.js 进程(通常是长期在后台运行的,或者可交互的,需要能控制),执行某个 js 文件,
|
|
197
|
+
返回 {@link SubProcess} 对象 (继承自 ChildProcess),可以做 .kill() 等操作
|
|
198
|
+
- js: .js 路径 (相对路径根据 cwd 解析)
|
|
199
|
+
- args: `[]` 参数列表
|
|
200
|
+
- options?: {@link CallNodeJsOptions} 继承自 {@link CallOptions} 和 {@link NodeJsOptions}
|
|
201
|
+
- inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
|
|
202
|
+
- break?: 在第一行停止 */
|
|
203
|
+
export declare function launch_nodejs(js: string, args?: string[], options?: CallNodeJsOptions): Promise<SubProcess<string>>;
|
|
204
|
+
/** 可选的向子进程输入内容,并等待子进程输出特定的内容,类似一次无返回值,仅有完成状态的 rpc 调用
|
|
205
|
+
根据文本内容匹配其实不太好,实在是没有别的 rpc 方法再使用吧
|
|
206
|
+
- ps: 子进程
|
|
207
|
+
- pattern: 匹配内容,支持 string 和 RegExp
|
|
208
|
+
- input?: 要输入的内容
|
|
209
|
+
- timeout?: 等待超时时间 */
|
|
210
|
+
export declare function wait_output(ps: SubProcess, pattern: string | RegExp, input?: string, _timeout?: number): Promise<void>;
|
|
169
211
|
export {};
|
package/process.js
CHANGED
|
@@ -2,13 +2,19 @@ import { spawn } from 'child_process';
|
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import node_sea from 'node:sea';
|
|
4
4
|
import "./prototype.js";
|
|
5
|
-
import { inspect, DecoderStream, filter_values, check, colored } from "./utils.js";
|
|
5
|
+
import { inspect, DecoderStream, filter_values, check, colored, timeout } from "./utils.js";
|
|
6
6
|
export const sea = node_sea.isSea();
|
|
7
7
|
export const exe_nodejs = process.execPath.fp;
|
|
8
8
|
export const platform = os.platform();
|
|
9
9
|
export const username = os.userInfo().username;
|
|
10
10
|
export const noprint = { print: false };
|
|
11
11
|
export const print_no_command = { print: { command: false, code: false, stdout: true, stderr: true } };
|
|
12
|
+
export function get_command(exe, args) {
|
|
13
|
+
return (short_exe_names[exe] || exe.quote_if_space()) +
|
|
14
|
+
(args?.length
|
|
15
|
+
? ` ${args.map(arg => arg.quote_if_space()).join(' ')}`
|
|
16
|
+
: '');
|
|
17
|
+
}
|
|
12
18
|
async function prepare_spawn(detached, exe, args, { cwd, window: _window, envs,
|
|
13
19
|
// @ts-ignore
|
|
14
20
|
input, fp_stdin, fp_stdout, fp_stderr, print = true, proxy,
|
|
@@ -78,13 +84,7 @@ stdio }) {
|
|
|
78
84
|
stdout: print,
|
|
79
85
|
stderr: print
|
|
80
86
|
};
|
|
81
|
-
const command = (
|
|
82
|
-
(args.length
|
|
83
|
-
? ' ' +
|
|
84
|
-
args
|
|
85
|
-
.map(arg => arg.quote_if_space())
|
|
86
|
-
.join(' ')
|
|
87
|
-
: '');
|
|
87
|
+
const command = get_command(exe, args);
|
|
88
88
|
if (print.command)
|
|
89
89
|
console.log(command.blue);
|
|
90
90
|
return {
|
|
@@ -102,7 +102,28 @@ stdio }) {
|
|
|
102
102
|
close_all_handles: opened_handles.length ? close_all_handles : undefined
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
function to_subprocess(child, { title, exe, args, command, }) {
|
|
106
|
+
if (title)
|
|
107
|
+
child.title = title;
|
|
108
|
+
child.exe = exe;
|
|
109
|
+
child.args = args || [];
|
|
110
|
+
child.command = command;
|
|
111
|
+
Object.defineProperties(child, {
|
|
112
|
+
finished: {
|
|
113
|
+
get() {
|
|
114
|
+
return this.exitCode !== null;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
running: {
|
|
118
|
+
get() {
|
|
119
|
+
return this.exitCode === null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
return child;
|
|
124
|
+
}
|
|
125
|
+
/** 启动独立 (detached) 的进程,不受当前 node.js 进程退出的影响,重定向 stdio 到文件或直接忽略
|
|
126
|
+
使用 windowsHide 选项避免创建命令行窗口
|
|
106
127
|
- exe: .exe 路径或文件名 (建议使用完整路径,跳过 path 搜索,性能更高)
|
|
107
128
|
- args?: `[ ]` 参数列表
|
|
108
129
|
- options?:
|
|
@@ -115,9 +136,9 @@ stdio }) {
|
|
|
115
136
|
- fp_stdout?: 使用文件作为标准输出,设置 stdio 中对应的值
|
|
116
137
|
- fp_stderr?: 使用文件作为标准错误,设置 stdio 中对应的值 */
|
|
117
138
|
export async function start(exe, args = [], options = {}) {
|
|
118
|
-
const { spawn_options, close_all_handles } = await prepare_spawn(true, exe, args, options);
|
|
139
|
+
const { spawn_options, close_all_handles, command } = await prepare_spawn(true, exe, args, options);
|
|
119
140
|
try {
|
|
120
|
-
let child = spawn(exe, args, spawn_options);
|
|
141
|
+
let child = to_subprocess(spawn(exe, args, spawn_options), { exe, command, args, title: options.title });
|
|
121
142
|
child.unref();
|
|
122
143
|
return child;
|
|
123
144
|
}
|
|
@@ -157,7 +178,7 @@ export async function call(exe, args = [], options = {}) {
|
|
|
157
178
|
const { print, spawn_options, close_all_handles, command } = await prepare_spawn(false, exe, args, options);
|
|
158
179
|
let child;
|
|
159
180
|
try {
|
|
160
|
-
child = spawn(exe, args, spawn_options);
|
|
181
|
+
child = to_subprocess(spawn(exe, args, spawn_options), { exe, args, command, title: options.title });
|
|
161
182
|
}
|
|
162
183
|
finally {
|
|
163
184
|
if (close_all_handles)
|
|
@@ -253,14 +274,30 @@ export async function call(exe, args = [], options = {}) {
|
|
|
253
274
|
});
|
|
254
275
|
}
|
|
255
276
|
};
|
|
256
|
-
if (throw_code &&
|
|
277
|
+
if (throw_code && signal)
|
|
257
278
|
throw new CallError(result);
|
|
258
279
|
return result;
|
|
259
280
|
}
|
|
281
|
+
export async function launch(exe, args, options) {
|
|
282
|
+
let presult;
|
|
283
|
+
let ps = await new Promise((resolve, reject) => {
|
|
284
|
+
try {
|
|
285
|
+
presult = call(exe, args, {
|
|
286
|
+
on_child: resolve,
|
|
287
|
+
...options,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
reject(error);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
ps.presult = presult;
|
|
295
|
+
return ps;
|
|
296
|
+
}
|
|
260
297
|
/** 调用 node <js> 并等待结果
|
|
261
298
|
- js: .js 路径 (相对路径根据 cwd 解析)
|
|
262
299
|
- args?: `[ ]` 参数列表
|
|
263
|
-
- options?:
|
|
300
|
+
- options?: {@link CallNodeJsOptions} 继承自 {@link CallOptions} 和 {@link NodeJsOptions}
|
|
264
301
|
- cwd?: `'T:/'`
|
|
265
302
|
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量
|
|
266
303
|
- encoding?: `'utf-8'` 子进程输出编码
|
|
@@ -269,30 +306,69 @@ export async function call(exe, args = [], options = {}) {
|
|
|
269
306
|
- throw_code?: `true` code 不为 0 时是否抛出异常
|
|
270
307
|
- inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
|
|
271
308
|
- break?: break at first line */
|
|
272
|
-
export async function call_nodejs(js, args
|
|
273
|
-
return call(exe_nodejs,
|
|
274
|
-
'--enable-source-maps',
|
|
275
|
-
...inspect ? [`--inspect${_break ? '-brk' : ''}=localhost:${inspect === true ? 9229 : inspect}`] : [],
|
|
276
|
-
js,
|
|
277
|
-
...args
|
|
278
|
-
], options);
|
|
309
|
+
export async function call_nodejs(js, args, options) {
|
|
310
|
+
return call(exe_nodejs, get_nodejs_args(js, args, options), options);
|
|
279
311
|
}
|
|
280
|
-
/**
|
|
312
|
+
/** 启动独立的 node.js 进程 (detached),执行某个 js 文件
|
|
281
313
|
- js: .js 路径 (相对路径根据 cwd 解析)
|
|
282
314
|
- args: `[]` 参数列表
|
|
283
|
-
- options
|
|
284
|
-
- cwd?: `'T:/'`
|
|
285
|
-
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量
|
|
286
|
-
- print?: `true` print 选项,支持设置细项
|
|
315
|
+
- options?: {@link StartNodeJsOptions} 继承自 {@link StartOptions} 和 {@link NodeJsOptions}
|
|
287
316
|
- inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
|
|
288
|
-
- break?:
|
|
289
|
-
export async function start_nodejs(js, args
|
|
290
|
-
return start(exe_nodejs,
|
|
317
|
+
- break?: 在第一行停止 */
|
|
318
|
+
export async function start_nodejs(js, args, options) {
|
|
319
|
+
return start(exe_nodejs, get_nodejs_args(js, args, options), options);
|
|
320
|
+
}
|
|
321
|
+
/** 启动子 node.js 进程(通常是长期在后台运行的,或者可交互的,需要能控制),执行某个 js 文件,
|
|
322
|
+
返回 {@link SubProcess} 对象 (继承自 ChildProcess),可以做 .kill() 等操作
|
|
323
|
+
- js: .js 路径 (相对路径根据 cwd 解析)
|
|
324
|
+
- args: `[]` 参数列表
|
|
325
|
+
- options?: {@link CallNodeJsOptions} 继承自 {@link CallOptions} 和 {@link NodeJsOptions}
|
|
326
|
+
- inspect?: nodejs debugger port, 填 9229 端口 (或者传 true) 可以用临时配置来调试
|
|
327
|
+
- break?: 在第一行停止 */
|
|
328
|
+
export async function launch_nodejs(js, args, options) {
|
|
329
|
+
return launch(exe_nodejs, get_nodejs_args(js, args, options), options);
|
|
330
|
+
}
|
|
331
|
+
function get_nodejs_args(js, args = [], { inspect, break: _break } = {}) {
|
|
332
|
+
return [
|
|
291
333
|
'--enable-source-maps',
|
|
292
334
|
...inspect ? [`--inspect${_break ? '-brk' : ''}=localhost:${inspect === true ? 9229 : inspect}`] : [],
|
|
293
335
|
js,
|
|
294
336
|
...args
|
|
295
|
-
]
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
/** 可选的向子进程输入内容,并等待子进程输出特定的内容,类似一次无返回值,仅有完成状态的 rpc 调用
|
|
340
|
+
根据文本内容匹配其实不太好,实在是没有别的 rpc 方法再使用吧
|
|
341
|
+
- ps: 子进程
|
|
342
|
+
- pattern: 匹配内容,支持 string 和 RegExp
|
|
343
|
+
- input?: 要输入的内容
|
|
344
|
+
- timeout?: 等待超时时间 */
|
|
345
|
+
export async function wait_output(ps, pattern, input, _timeout) {
|
|
346
|
+
const { stdin, stdout } = ps;
|
|
347
|
+
const matcher = typeof pattern === 'string'
|
|
348
|
+
? (chunk) => chunk.includes(pattern)
|
|
349
|
+
: (chunk) => pattern.test(chunk);
|
|
350
|
+
let on_data;
|
|
351
|
+
let pfound = new Promise(resolve => {
|
|
352
|
+
on_data = (chunk) => {
|
|
353
|
+
if (matcher(chunk)) {
|
|
354
|
+
stdout.off('data', on_data);
|
|
355
|
+
resolve();
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
stdout.on('data', on_data);
|
|
359
|
+
if (input)
|
|
360
|
+
stdin.write(input);
|
|
361
|
+
});
|
|
362
|
+
if (_timeout)
|
|
363
|
+
try {
|
|
364
|
+
await timeout(_timeout, pfound);
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
stdout.off('data', on_data);
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
else
|
|
371
|
+
await pfound;
|
|
296
372
|
}
|
|
297
373
|
const short_exe_names = {
|
|
298
374
|
[exe_nodejs]: 'node',
|
package/server.d.ts
CHANGED
|
@@ -53,7 +53,6 @@ export declare class Server {
|
|
|
53
53
|
websocket_server?: WebSocketServer;
|
|
54
54
|
/** 设置后会启用 websocket rpc */
|
|
55
55
|
remote?: Remote;
|
|
56
|
-
colors: boolean;
|
|
57
56
|
/** 输出日志时包含日期 */
|
|
58
57
|
log_date: boolean;
|
|
59
58
|
/** 启用后增加 stdio 订阅相关的 remote.funcs */
|
|
@@ -65,6 +64,7 @@ export declare class Server {
|
|
|
65
64
|
/** 原始 process.stdout.write 函数 bind 后的备份 */
|
|
66
65
|
stdout_write: Function;
|
|
67
66
|
stderr_write: Function;
|
|
67
|
+
url_width: 52;
|
|
68
68
|
constructor({ name, print, http, http2, http_port, http2_port, fpd_certs, default_hostnames, remote, funcs, stdio_subscribable, log_date }: {
|
|
69
69
|
name: string;
|
|
70
70
|
print?: boolean | {
|
package/server.js
CHANGED
|
@@ -4,7 +4,6 @@ import { createSecureContext } from 'tls';
|
|
|
4
4
|
import zlib from 'zlib';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import { buffer as stream_to_buffer } from 'stream/consumers';
|
|
7
|
-
import util from 'util';
|
|
8
7
|
import node_sea from 'node:sea';
|
|
9
8
|
import { default as Koa } from 'koa';
|
|
10
9
|
import KoaCors from '@koa/cors';
|
|
@@ -15,7 +14,7 @@ import { contentType as get_content_type } from 'mime-types';
|
|
|
15
14
|
// --- my libs
|
|
16
15
|
import { t } from "./i18n/instance.js";
|
|
17
16
|
import { request as _request, Remote } from "./net.js";
|
|
18
|
-
import { inspect,
|
|
17
|
+
import { inspect, check, range_to_numbers, encode, filter_keys, filter_values, consume_stream } from "./utils.js";
|
|
19
18
|
import { flist, fread, fstat } from "./file.js";
|
|
20
19
|
import { exe_nodejs, sea } from "./process.js";
|
|
21
20
|
// ------------ my server
|
|
@@ -57,7 +56,6 @@ export class Server {
|
|
|
57
56
|
websocket_server;
|
|
58
57
|
/** 设置后会启用 websocket rpc */
|
|
59
58
|
remote;
|
|
60
|
-
colors = util.inspect.defaultOptions.colors;
|
|
61
59
|
/** 输出日志时包含日期 */
|
|
62
60
|
log_date = false;
|
|
63
61
|
/** 启用后增加 stdio 订阅相关的 remote.funcs */
|
|
@@ -66,6 +64,7 @@ export class Server {
|
|
|
66
64
|
/** 原始 process.stdout.write 函数 bind 后的备份 */
|
|
67
65
|
stdout_write;
|
|
68
66
|
stderr_write;
|
|
67
|
+
url_width = 52;
|
|
69
68
|
constructor({ name, print, http, http2, http_port, http2_port, fpd_certs, default_hostnames, remote, funcs, stdio_subscribable, log_date }) {
|
|
70
69
|
this.name = name;
|
|
71
70
|
if (print !== undefined) {
|
|
@@ -278,22 +277,23 @@ export class Server {
|
|
|
278
277
|
// url 只有路径部分
|
|
279
278
|
const { url, headers, headers: { host = '' }, } = request;
|
|
280
279
|
if (this.print.logs) {
|
|
281
|
-
|
|
282
|
-
console.log(
|
|
280
|
+
let s =
|
|
283
281
|
// 时间
|
|
284
282
|
`${this.log_date ? new Date().to_str() : new Date().to_time_str()} ` +
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
// ua
|
|
288
|
-
this.format_ua(headers).limit(56) + ' ' +
|
|
289
|
-
// https/2.0
|
|
290
|
-
`${this.colors ? 'websocket'.limit(10).magenta : 'websocket'.limit(10)} ` +
|
|
291
|
-
// method
|
|
292
|
-
''.limit(6) + ' ' +
|
|
283
|
+
// wss://
|
|
284
|
+
`${`ws${socket.encrypted ? 's' : ''}`.yellow}://` +
|
|
293
285
|
// host
|
|
294
|
-
|
|
286
|
+
host +
|
|
295
287
|
// path
|
|
296
|
-
|
|
288
|
+
url;
|
|
289
|
+
s = s.pad(this.url_width);
|
|
290
|
+
// ip
|
|
291
|
+
s += ` <- ${request.socket.remoteAddress.strip_if_start('::ffff:')}`;
|
|
292
|
+
// ua
|
|
293
|
+
const ua = this.format_ua(headers);
|
|
294
|
+
if (ua)
|
|
295
|
+
s += `/${ua}`;
|
|
296
|
+
console.log(s);
|
|
297
297
|
}
|
|
298
298
|
switch (url) {
|
|
299
299
|
case '/':
|
|
@@ -304,7 +304,7 @@ export class Server {
|
|
|
304
304
|
return;
|
|
305
305
|
default:
|
|
306
306
|
if (this.print.logs)
|
|
307
|
-
console.log(
|
|
307
|
+
console.log(`未知路径的 upgrade 请求: ${url}`.red);
|
|
308
308
|
socket.destroy();
|
|
309
309
|
}
|
|
310
310
|
}
|
|
@@ -381,7 +381,6 @@ export class Server {
|
|
|
381
381
|
return true;
|
|
382
382
|
}
|
|
383
383
|
logger(ctx) {
|
|
384
|
-
const { colors } = this;
|
|
385
384
|
const { request } = ctx;
|
|
386
385
|
const { query: queries, body, path, _path, protocol, host, req: { httpVersion: http_version }, ip, headers, } = request;
|
|
387
386
|
let { method } = request;
|
|
@@ -390,28 +389,22 @@ export class Server {
|
|
|
390
389
|
let s = '';
|
|
391
390
|
// 时间
|
|
392
391
|
s += `${this.log_date ? new Date().to_str() : new Date().to_time_str()} `;
|
|
393
|
-
// ip(位置)
|
|
394
|
-
s += (ip || '').limit(40) + ' ';
|
|
395
|
-
// ua
|
|
396
|
-
s += this.process_ua(ctx).limit(56) + ' ';
|
|
397
|
-
// https/2.0
|
|
398
|
-
s += `${`${protocol.limit(5)} ${http_version}`.limit(10)} `;
|
|
399
392
|
// method
|
|
400
393
|
method = method.toLowerCase();
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
s +=
|
|
394
|
+
if (method !== 'get')
|
|
395
|
+
s += `${method.yellow} `;
|
|
396
|
+
// http, https, http2, websocket
|
|
397
|
+
s += `${http_version === '2.0' ? 'https'.magenta : protocol}://`;
|
|
405
398
|
// host
|
|
406
|
-
s +=
|
|
399
|
+
s += host;
|
|
407
400
|
// path
|
|
408
|
-
s += (
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
401
|
+
s += path.toLowerCase() !== _path.toLowerCase()
|
|
402
|
+
? _path === '/'
|
|
403
|
+
? '/'.blue
|
|
404
|
+
: `${_path} -> ${path}`
|
|
405
|
+
: path.includes('.')
|
|
406
|
+
? path
|
|
407
|
+
: path.yellow;
|
|
415
408
|
// range
|
|
416
409
|
let range = headers.range;
|
|
417
410
|
if (headers.range) {
|
|
@@ -422,14 +415,21 @@ export class Server {
|
|
|
422
415
|
end = Number(end).to_fsize_str();
|
|
423
416
|
s += ` (${start} - ${end || ''})`;
|
|
424
417
|
}
|
|
418
|
+
s += ' ';
|
|
425
419
|
// queries
|
|
426
420
|
if (Object.keys(queries).length) {
|
|
427
|
-
|
|
421
|
+
s += inspect(queries, { compact: true })
|
|
428
422
|
.replace('[Object: null prototype] ', '')
|
|
429
423
|
.strip_if_end('\n');
|
|
430
|
-
s +=
|
|
431
|
-
s += t;
|
|
424
|
+
s += ' ';
|
|
432
425
|
}
|
|
426
|
+
s = s.pad(this.url_width);
|
|
427
|
+
// ip
|
|
428
|
+
s += ` <- ${ip.strip_if_start('::ffff:')}`;
|
|
429
|
+
// ua
|
|
430
|
+
const ua = this.process_ua(ctx);
|
|
431
|
+
if (ua)
|
|
432
|
+
s += `/${ua}`;
|
|
433
433
|
// body
|
|
434
434
|
if (body && Object.keys(body).length)
|
|
435
435
|
s += '\n' + inspect(body).replace('[Object: null prototype] ', '');
|
package/utils.browser.d.ts
CHANGED
|
@@ -25,10 +25,12 @@ export declare class TimeoutError extends Error {
|
|
|
25
25
|
name: "TimeoutError";
|
|
26
26
|
}
|
|
27
27
|
/** 在指定的时间 (milliseconds) 内运行某个任务,超时之后
|
|
28
|
-
-
|
|
29
|
-
-
|
|
28
|
+
- action?: 要等待运行的任务, async function 或 promise
|
|
29
|
+
- on_timeout?: 超时后调用的函数
|
|
30
|
+
- 如果传入了 on_timeout 参数: 调用 on_timeout,然后 timeout 函数正常返回 null
|
|
31
|
+
- 如果没传入 on_timeout 参数: 抛出 TimeoutError
|
|
30
32
|
- print?: 打印已超时任务的错误 */
|
|
31
|
-
export declare function timeout<TReturn>(milliseconds: number, action: () => Promise<TReturn
|
|
33
|
+
export declare function timeout<TReturn>(milliseconds: number, action: Promise<TReturn> | (() => Promise<TReturn>), on_timeout?: () => void | Promise<void>, print?: boolean): Promise<TReturn>;
|
|
32
34
|
/** https://stackoverflow.com/questions/63297164/how-to-only-accept-arraybuffer-as-parameter */
|
|
33
35
|
export type StrictArrayBuffer = ArrayBuffer & {
|
|
34
36
|
buffer?: undefined;
|
package/utils.browser.js
CHANGED
|
@@ -77,8 +77,10 @@ export class TimeoutError extends Error {
|
|
|
77
77
|
name = 'TimeoutError';
|
|
78
78
|
}
|
|
79
79
|
/** 在指定的时间 (milliseconds) 内运行某个任务,超时之后
|
|
80
|
-
-
|
|
81
|
-
-
|
|
80
|
+
- action?: 要等待运行的任务, async function 或 promise
|
|
81
|
+
- on_timeout?: 超时后调用的函数
|
|
82
|
+
- 如果传入了 on_timeout 参数: 调用 on_timeout,然后 timeout 函数正常返回 null
|
|
83
|
+
- 如果没传入 on_timeout 参数: 抛出 TimeoutError
|
|
82
84
|
- print?: 打印已超时任务的错误 */
|
|
83
85
|
export async function timeout(milliseconds, action, on_timeout, print = true) {
|
|
84
86
|
const error = new TimeoutError();
|
|
@@ -108,7 +110,7 @@ export async function timeout(milliseconds, action, on_timeout, print = true) {
|
|
|
108
110
|
})();
|
|
109
111
|
(async () => {
|
|
110
112
|
try {
|
|
111
|
-
resolve(await action());
|
|
113
|
+
resolve(await (typeof action === 'function' ? action() : action));
|
|
112
114
|
}
|
|
113
115
|
catch (error) {
|
|
114
116
|
if (rejected) {
|
package/utils.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import util from 'util';
|
|
|
3
3
|
import type { TimerOptions } from 'timers';
|
|
4
4
|
import type Vinyl from 'vinyl';
|
|
5
5
|
import { type Mapper } from './prototype.ts';
|
|
6
|
-
/** `
|
|
7
|
-
export declare const output_width =
|
|
6
|
+
/** `180` 输出字符宽度 */
|
|
7
|
+
export declare const output_width = 180;
|
|
8
8
|
export declare function set_inspect_options(colors?: boolean): void;
|
|
9
9
|
export declare function assert<T>(assertion: T, message?: string): T;
|
|
10
10
|
/** 做参数校验,逻辑检查 */
|
|
@@ -91,10 +91,12 @@ export declare class TimeoutError extends Error {
|
|
|
91
91
|
name: "TimeoutError";
|
|
92
92
|
}
|
|
93
93
|
/** 在指定的时间 (milliseconds) 内运行某个任务,超时之后
|
|
94
|
-
-
|
|
95
|
-
-
|
|
94
|
+
- action?: 要等待运行的任务, async function 或 promise
|
|
95
|
+
- on_timeout?: 超时后调用的函数
|
|
96
|
+
- 如果传入了 on_timeout 参数: 调用 on_timeout,然后 timeout 函数正常返回 null
|
|
97
|
+
- 如果没传入 on_timeout 参数: 抛出 TimeoutError
|
|
96
98
|
- print?: 打印已超时任务的错误 */
|
|
97
|
-
export declare function timeout<TReturn>(milliseconds: number, action: () => Promise<TReturn
|
|
99
|
+
export declare function timeout<TReturn>(milliseconds: number, action: Promise<TReturn> | (() => Promise<TReturn>), on_timeout?: () => void | Promise<void>, print?: boolean): Promise<TReturn>;
|
|
98
100
|
/** https://stackoverflow.com/questions/63297164/how-to-only-accept-arraybuffer-as-parameter */
|
|
99
101
|
export type StrictArrayBuffer = ArrayBuffer & {
|
|
100
102
|
buffer?: undefined;
|
package/utils.js
CHANGED
|
@@ -3,8 +3,8 @@ import util from 'util';
|
|
|
3
3
|
import timers from 'timers/promises';
|
|
4
4
|
import { t } from "./i18n/instance.js";
|
|
5
5
|
import { build_mapper, not_empty, is_key_type, noop, ident } from "./prototype.js";
|
|
6
|
-
/** `
|
|
7
|
-
export const output_width =
|
|
6
|
+
/** `180` 输出字符宽度 */
|
|
7
|
+
export const output_width = 180;
|
|
8
8
|
export function set_inspect_options(colors = true) {
|
|
9
9
|
util.inspect.defaultOptions.maxArrayLength = 40;
|
|
10
10
|
util.inspect.defaultOptions.maxStringLength = 10000;
|
|
@@ -281,8 +281,10 @@ export class TimeoutError extends Error {
|
|
|
281
281
|
name = 'TimeoutError';
|
|
282
282
|
}
|
|
283
283
|
/** 在指定的时间 (milliseconds) 内运行某个任务,超时之后
|
|
284
|
-
-
|
|
285
|
-
-
|
|
284
|
+
- action?: 要等待运行的任务, async function 或 promise
|
|
285
|
+
- on_timeout?: 超时后调用的函数
|
|
286
|
+
- 如果传入了 on_timeout 参数: 调用 on_timeout,然后 timeout 函数正常返回 null
|
|
287
|
+
- 如果没传入 on_timeout 参数: 抛出 TimeoutError
|
|
286
288
|
- print?: 打印已超时任务的错误 */
|
|
287
289
|
export async function timeout(milliseconds, action, on_timeout, print = true) {
|
|
288
290
|
const error = new TimeoutError();
|
|
@@ -312,7 +314,7 @@ export async function timeout(milliseconds, action, on_timeout, print = true) {
|
|
|
312
314
|
})();
|
|
313
315
|
(async () => {
|
|
314
316
|
try {
|
|
315
|
-
resolve(await action());
|
|
317
|
+
resolve(await (typeof action === 'function' ? action() : action));
|
|
316
318
|
}
|
|
317
319
|
catch (error) {
|
|
318
320
|
if (rejected) {
|