xshell 1.0.71 → 1.0.73
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 -1
- package/file.js +7 -10
- package/git.d.ts +6 -1
- package/git.js +17 -5
- package/i18n/dict.json +12 -0
- package/i18n/i18n-scan.js +0 -0
- package/net.browser.js +3 -5
- package/net.js +3 -5
- package/package.json +132 -126
- package/server.d.ts +3 -1
- package/server.js +27 -3
- package/utils.browser.d.ts +5 -0
- package/utils.browser.js +12 -0
- package/utils.d.ts +9 -0
- package/utils.js +30 -0
- package/xshell.png +0 -0
package/file.d.ts
CHANGED
|
@@ -105,8 +105,9 @@ export declare function ffstat(handle: fsp.FileHandle): Promise<fs.BigIntStats>;
|
|
|
105
105
|
|
|
106
106
|
返回是否实际进行了删除操作
|
|
107
107
|
Returns whether the delete operation actually took place */
|
|
108
|
-
export declare function fdelete(fp: string, { print }?: {
|
|
108
|
+
export declare function fdelete(fp: string, { print, red }?: {
|
|
109
109
|
print?: boolean;
|
|
110
|
+
red?: boolean;
|
|
110
111
|
}): Promise<boolean>;
|
|
111
112
|
/** 复制文件或文件夹
|
|
112
113
|
会在因不存在父文件夹导致复制失败时,自动创建父文件夹,并再次尝试复制
|
package/file.js
CHANGED
|
@@ -194,25 +194,22 @@ export async function ffstat(handle) {
|
|
|
194
194
|
|
|
195
195
|
返回是否实际进行了删除操作
|
|
196
196
|
Returns whether the delete operation actually took place */
|
|
197
|
-
export async function fdelete(fp, { print = true } = {}) {
|
|
197
|
+
export async function fdelete(fp, { print = true, red = true } = {}) {
|
|
198
198
|
assert(fp.length >= 6, `fp: ${fp} ${t('不能太短,防止误删文件')}`);
|
|
199
199
|
assert(path.isAbsolute(fp), t('fp 必须是绝对路径'));
|
|
200
|
+
const { isdir } = fp;
|
|
200
201
|
try {
|
|
201
202
|
await fsp.rm(fp, { recursive: true });
|
|
202
|
-
if (print)
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
console.log((t('删除了文件: ') + fp).red);
|
|
203
|
+
if (print) {
|
|
204
|
+
const message = `${isdir ? t('删除了文件夹') : t('删除了文件')} ${fp}`;
|
|
205
|
+
console.log(red ? message.red : message);
|
|
206
|
+
}
|
|
207
207
|
return true;
|
|
208
208
|
}
|
|
209
209
|
catch (error) {
|
|
210
210
|
if (error.code === 'ENOENT') {
|
|
211
211
|
if (print)
|
|
212
|
-
|
|
213
|
-
console.log(t('文件夹已不存在: ') + fp);
|
|
214
|
-
else
|
|
215
|
-
console.log(t('文件已不存在: ') + fp);
|
|
212
|
+
console.log(`${isdir ? t(`已不存在文件`) : t('已不存在文件夹')} ${fp}`);
|
|
216
213
|
return false;
|
|
217
214
|
}
|
|
218
215
|
throw error;
|
package/git.d.ts
CHANGED
|
@@ -25,7 +25,10 @@ export declare class Git {
|
|
|
25
25
|
hash: string;
|
|
26
26
|
message: string;
|
|
27
27
|
}[]>;
|
|
28
|
-
fetch(print?:
|
|
28
|
+
fetch({ print, remote }?: {
|
|
29
|
+
print?: boolean;
|
|
30
|
+
remote?: string;
|
|
31
|
+
}): Promise<void>;
|
|
29
32
|
/** - branch?: `'main'` */
|
|
30
33
|
checkout(branch?: 'main'): Promise<void>;
|
|
31
34
|
checkout(branch?: string): Promise<void>;
|
|
@@ -45,4 +48,6 @@ export declare class Git {
|
|
|
45
48
|
update(branch?: 'main'): Promise<void>;
|
|
46
49
|
update(branch: string): Promise<void>;
|
|
47
50
|
update(branch: string, remote?: string): Promise<void>;
|
|
51
|
+
/** - message?: stash description */
|
|
52
|
+
stash(message?: string): Promise<import("./process.js").CallResult<string>>;
|
|
48
53
|
}
|
package/git.js
CHANGED
|
@@ -76,8 +76,12 @@ export class Git {
|
|
|
76
76
|
};
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
|
-
async fetch(print = true) {
|
|
80
|
-
await this.call([
|
|
79
|
+
async fetch({ print = true, remote } = {}) {
|
|
80
|
+
await this.call([
|
|
81
|
+
'fetch',
|
|
82
|
+
remote || '--all',
|
|
83
|
+
'--prune'
|
|
84
|
+
], { print: { code: false, command: false, stdout: true, stderr: true } });
|
|
81
85
|
if (print)
|
|
82
86
|
console.log('更新远程分支成功');
|
|
83
87
|
}
|
|
@@ -152,10 +156,18 @@ export class Git {
|
|
|
152
156
|
await this.checkout(target_branch);
|
|
153
157
|
await this.merge(branch, true);
|
|
154
158
|
}
|
|
155
|
-
async update(branch = 'main', remote
|
|
156
|
-
await this.fetch();
|
|
159
|
+
async update(branch = 'main', remote) {
|
|
160
|
+
await this.fetch({ remote });
|
|
157
161
|
await this.checkout(branch);
|
|
158
|
-
await this.merge(`${remote}/${branch}`);
|
|
162
|
+
await this.merge(`${remote || 'origin'}/${branch}`);
|
|
163
|
+
}
|
|
164
|
+
/** - message?: stash description */
|
|
165
|
+
async stash(message) {
|
|
166
|
+
return this.call([
|
|
167
|
+
'stash',
|
|
168
|
+
'push',
|
|
169
|
+
...message ? ['--message', message] : [],
|
|
170
|
+
]);
|
|
159
171
|
}
|
|
160
172
|
}
|
|
161
173
|
//# sourceMappingURL=git.js.map
|
package/i18n/dict.json
CHANGED
|
@@ -361,5 +361,17 @@
|
|
|
361
361
|
},
|
|
362
362
|
"{{name}} 启动成功,正在监听 {{ports}} 端口": {
|
|
363
363
|
"en": "{{name}} started successfully and is listening on {{ports}} port"
|
|
364
|
+
},
|
|
365
|
+
"删除了文件夹": {
|
|
366
|
+
"en": "deleted folder"
|
|
367
|
+
},
|
|
368
|
+
"删除了文件": {
|
|
369
|
+
"en": "deleted file"
|
|
370
|
+
},
|
|
371
|
+
"已不存在文件": {
|
|
372
|
+
"en": "file no longer exists:"
|
|
373
|
+
},
|
|
374
|
+
"已不存在文件夹": {
|
|
375
|
+
"en": "folder no longer exists:"
|
|
364
376
|
}
|
|
365
377
|
}
|
package/i18n/i18n-scan.js
CHANGED
|
File without changes
|
package/net.browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t } from './i18n/instance.js';
|
|
2
2
|
import './prototype.browser.js'; // to_time_str()
|
|
3
|
-
import { assert, concat, genid, delay, Lock } from './utils.browser.js';
|
|
3
|
+
import { assert, concat, genid, delay, Lock, encode, decode } from './utils.browser.js';
|
|
4
4
|
// ------------------------------------ fetch, request
|
|
5
5
|
const drop_request_headers = new Set([
|
|
6
6
|
// : 开头的 key
|
|
@@ -143,8 +143,6 @@ export async function request_json(url, options) {
|
|
|
143
143
|
throw error;
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
let decoder = new TextDecoder();
|
|
147
|
-
let encoder = new TextEncoder();
|
|
148
146
|
export class WebSocketConnectionError extends Error {
|
|
149
147
|
name = 'WebSocketConnectionError';
|
|
150
148
|
// 这里不保留 websocket 引用,防止循环引用导致 JSON 序列化失败
|
|
@@ -280,7 +278,7 @@ export class Remote {
|
|
|
280
278
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
281
279
|
const len_json = dv.getUint32(0, true);
|
|
282
280
|
let offset = 4 + len_json;
|
|
283
|
-
let message = JSON.parse(
|
|
281
|
+
let message = JSON.parse(decode(buffer.subarray(4, offset)));
|
|
284
282
|
if (message.bins) {
|
|
285
283
|
const { bins } = message;
|
|
286
284
|
let { data } = message;
|
|
@@ -318,7 +316,7 @@ export class Remote {
|
|
|
318
316
|
...bins.length ? { bins } : {},
|
|
319
317
|
};
|
|
320
318
|
// 有可能 data_json 含有循环引用导致 JSON.stringify 报错
|
|
321
|
-
const str_json =
|
|
319
|
+
const str_json = encode(JSON.stringify(data_json));
|
|
322
320
|
let dv = new DataView(new ArrayBuffer(4));
|
|
323
321
|
dv.setUint32(0, str_json.length, true);
|
|
324
322
|
return concat([dv, str_json, ...bufs]);
|
package/net.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t } from './i18n/instance.js';
|
|
2
2
|
import './prototype.js';
|
|
3
|
-
import { inspect, concat, assert, genid, delay, Lock } from './utils.js';
|
|
3
|
+
import { inspect, concat, assert, genid, delay, Lock, encode, decode } from './utils.js';
|
|
4
4
|
export const WebSocketConnecting = 0;
|
|
5
5
|
export const WebSocketOpen = 1;
|
|
6
6
|
export const WebSocketClosing = 2;
|
|
@@ -286,8 +286,6 @@ export async function rpc(func, args, { url = 'http://localhost:8421/api/rpc', a
|
|
|
286
286
|
}
|
|
287
287
|
});
|
|
288
288
|
}
|
|
289
|
-
let decoder = new TextDecoder();
|
|
290
|
-
let encoder = new TextEncoder();
|
|
291
289
|
export class WebSocketConnectionError extends Error {
|
|
292
290
|
name = 'WebSocketConnectionError';
|
|
293
291
|
// 这里不保留 websocket 引用,防止循环引用导致 JSON 序列化失败
|
|
@@ -443,7 +441,7 @@ export class Remote {
|
|
|
443
441
|
const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
444
442
|
const len_json = dv.getUint32(0, true);
|
|
445
443
|
let offset = 4 + len_json;
|
|
446
|
-
let message = JSON.parse(
|
|
444
|
+
let message = JSON.parse(decode(buffer.subarray(4, offset)));
|
|
447
445
|
if (message.bins) {
|
|
448
446
|
const { bins } = message;
|
|
449
447
|
let { data } = message;
|
|
@@ -481,7 +479,7 @@ export class Remote {
|
|
|
481
479
|
...bins.length ? { bins } : {},
|
|
482
480
|
};
|
|
483
481
|
// 有可能 data_json 含有循环引用导致 JSON.stringify 报错
|
|
484
|
-
const str_json =
|
|
482
|
+
const str_json = encode(JSON.stringify(data_json));
|
|
485
483
|
let dv = new DataView(new ArrayBuffer(4));
|
|
486
484
|
dv.setUint32(0, str_json.length, true);
|
|
487
485
|
return concat([dv, str_json, ...bufs]);
|
package/package.json
CHANGED
|
@@ -1,128 +1,134 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
],
|
|
18
|
-
"engines": {
|
|
19
|
-
"node": ">=21.3.0",
|
|
20
|
-
"vscode": ">=1.81.0"
|
|
21
|
-
},
|
|
22
|
-
"author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
|
|
23
|
-
"publisher": "ShenHongFei",
|
|
24
|
-
"homepage": "https://www.npmjs.com/package/xshell",
|
|
25
|
-
"icon": "xshell.png",
|
|
26
|
-
"extensionKind": [
|
|
27
|
-
"workspace"
|
|
28
|
-
],
|
|
29
|
-
"activationEvents": [
|
|
30
|
-
"onStartupFinished"
|
|
31
|
-
],
|
|
32
|
-
"contributes": {
|
|
33
|
-
"commands": [
|
|
34
|
-
{
|
|
35
|
-
"command": "xshell_repl",
|
|
36
|
-
"title": "xshell_repl"
|
|
37
|
-
}
|
|
2
|
+
"name": "xshell",
|
|
3
|
+
"version": "1.0.73",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"xshell": "./xshell.js",
|
|
8
|
+
"i18n-scan": "./i18n/i18n-scan.js"
|
|
9
|
+
},
|
|
10
|
+
"description": "xshell is a shell designed to provide a brand new human-computer interaction experience.",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"shell",
|
|
13
|
+
"node",
|
|
14
|
+
"repl",
|
|
15
|
+
"i18n",
|
|
16
|
+
"interactive programming"
|
|
38
17
|
],
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=21.3.0",
|
|
20
|
+
"vscode": ">=1.81.0"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"package": "vsce package",
|
|
27
|
+
"publish:npm": "npm publish --access=public",
|
|
28
|
+
"publish:extension": "vsce publish"
|
|
29
|
+
},
|
|
30
|
+
"author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
|
|
31
|
+
"publisher": "ShenHongFei",
|
|
32
|
+
"homepage": "https://www.npmjs.com/package/xshell",
|
|
33
|
+
"icon": "xshell.png",
|
|
34
|
+
"extensionKind": [
|
|
35
|
+
"workspace"
|
|
36
|
+
],
|
|
37
|
+
"activationEvents": [
|
|
38
|
+
"onStartupFinished"
|
|
39
|
+
],
|
|
40
|
+
"contributes": {
|
|
41
|
+
"commands": [
|
|
42
|
+
{
|
|
43
|
+
"command": "xshell_repl",
|
|
44
|
+
"title": "xshell_repl"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"keybindings": [
|
|
48
|
+
{
|
|
49
|
+
"command": "xshell_repl",
|
|
50
|
+
"key": "ctrl+enter",
|
|
51
|
+
"when": "editorTextFocus && (editorLangId == 'typescript' || editorLangId == 'javascript' || editorLangId == 'typescriptreact' || editorLangId == 'javascriptreact')"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@babel/core": "^7.23.6",
|
|
57
|
+
"@babel/parser": "^7.23.6",
|
|
58
|
+
"@babel/traverse": "^7.23.6",
|
|
59
|
+
"@koa/cors": "^5.0.0",
|
|
60
|
+
"@types/ws": "^8.5.10",
|
|
61
|
+
"ali-oss": "^6.18.1",
|
|
62
|
+
"archiver": "^6.0.1",
|
|
63
|
+
"byte-size": "^8.1.1",
|
|
64
|
+
"chalk": "^5.3.0",
|
|
65
|
+
"chardet": "^2.0.0",
|
|
66
|
+
"cli-table3": "^0.6.3",
|
|
67
|
+
"cli-truncate": "^4.0.0",
|
|
68
|
+
"colors": "^1.4.0",
|
|
69
|
+
"commander": "^11.1.0",
|
|
70
|
+
"emoji-regex": "^10.3.0",
|
|
71
|
+
"fetch-cookie": "^2.1.0",
|
|
72
|
+
"gulp-sort": "^2.0.0",
|
|
73
|
+
"hash-string": "^1.0.0",
|
|
74
|
+
"i18next": "^23.7.9",
|
|
75
|
+
"i18next-scanner": "^4.4.0",
|
|
76
|
+
"js-cookie": "^3.0.5",
|
|
77
|
+
"koa": "^2.14.2",
|
|
78
|
+
"koa-compress": "^5.1.1",
|
|
79
|
+
"lodash": "^4.17.21",
|
|
80
|
+
"map-stream": "0.0.7",
|
|
81
|
+
"mime-types": "^2.1.35",
|
|
82
|
+
"ora": "^7.0.1",
|
|
83
|
+
"react": "^18.2.0",
|
|
84
|
+
"react-i18next": "^13.5.0",
|
|
85
|
+
"react-object-model": "^1.2.1",
|
|
86
|
+
"resolve-path": "^1.4.0",
|
|
87
|
+
"strip-ansi": "^7.1.0",
|
|
88
|
+
"through2": "^4.0.2",
|
|
89
|
+
"tough-cookie": "^4.1.3",
|
|
90
|
+
"tslib": "^2.6.2",
|
|
91
|
+
"typescript": "^5.3.3",
|
|
92
|
+
"ua-parser-js": "2.0.0-alpha.2",
|
|
93
|
+
"undici": "^6.0.1",
|
|
94
|
+
"vinyl": "^3.0.0",
|
|
95
|
+
"vinyl-fs": "^4.0.0",
|
|
96
|
+
"ws": "^8.15.1",
|
|
97
|
+
"xterm": "^5.3.0",
|
|
98
|
+
"xterm-addon-fit": "^0.8.0",
|
|
99
|
+
"xterm-addon-web-links": "^0.9.0",
|
|
100
|
+
"xterm-addon-webgl": "^0.16.0"
|
|
101
|
+
},
|
|
102
|
+
"devDependencies": {
|
|
103
|
+
"@babel/types": "^7.23.6",
|
|
104
|
+
"@types/ali-oss": "^6.16.11",
|
|
105
|
+
"@types/archiver": "^6.0.2",
|
|
106
|
+
"@types/babel__traverse": "^7.20.4",
|
|
107
|
+
"@types/byte-size": "^8.1.2",
|
|
108
|
+
"@types/chardet": "^0.8.3",
|
|
109
|
+
"@types/gulp-sort": "2.0.4",
|
|
110
|
+
"@types/js-cookie": "^3.0.6",
|
|
111
|
+
"@types/koa": "^2.13.12",
|
|
112
|
+
"@types/koa-compress": "^4.0.6",
|
|
113
|
+
"@types/lodash": "^4.14.202",
|
|
114
|
+
"@types/mime-types": "^2.1.4",
|
|
115
|
+
"@types/node": "^20.10.4",
|
|
116
|
+
"@types/react": "^18.2.45",
|
|
117
|
+
"@types/through2": "^2.0.41",
|
|
118
|
+
"@types/tough-cookie": "^4.0.5",
|
|
119
|
+
"@types/ua-parser-js": "^0.7.39",
|
|
120
|
+
"@types/vinyl-fs": "^3.0.5",
|
|
121
|
+
"@types/vscode": "^1.85.0",
|
|
122
|
+
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
123
|
+
"@typescript-eslint/parser": "^6.14.0",
|
|
124
|
+
"eslint": "^8.55.0",
|
|
125
|
+
"eslint-plugin-react": "^7.33.2",
|
|
126
|
+
"eslint-plugin-xlint": "^1.0.11"
|
|
127
|
+
},
|
|
128
|
+
"pnpm": {
|
|
129
|
+
"patchedDependencies": {
|
|
130
|
+
"@types/byte-size@8.1.2": "patches/@types__byte-size@8.1.2.patch",
|
|
131
|
+
"koa@2.14.2": "patches/koa@2.14.2.patch"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
package/server.d.ts
CHANGED
|
@@ -58,7 +58,6 @@ export declare class Server {
|
|
|
58
58
|
/** 原始 process.stdout.write 函数 bind 后的备份 */
|
|
59
59
|
stdout_write: Function;
|
|
60
60
|
stderr_write: Function;
|
|
61
|
-
encoder: TextEncoder;
|
|
62
61
|
constructor({ name, http, http2, http_port, http2_port, fpd_certs, default_hostnames, remote, funcs, stdio_subscribable, }: {
|
|
63
62
|
name: string;
|
|
64
63
|
http?: boolean;
|
|
@@ -121,4 +120,7 @@ export declare class Server {
|
|
|
121
120
|
absolute?: boolean;
|
|
122
121
|
download?: boolean;
|
|
123
122
|
}): Promise<string>;
|
|
123
|
+
/** - range: 取值为逗号分割的多个可用端口或端口区间 (不能含有空格,包含区间右值),比如:`8321,8322,8300-8310,11000-11999
|
|
124
|
+
- reverse?: `false` 在 range 内从后往前尝试 */
|
|
125
|
+
get_available_port(range: string, reverse?: boolean): Promise<number>;
|
|
124
126
|
}
|
package/server.js
CHANGED
|
@@ -8,7 +8,7 @@ import util from 'util';
|
|
|
8
8
|
// --- my libs
|
|
9
9
|
import { t } from './i18n/instance.js';
|
|
10
10
|
import { request as _request, Remote } from './net.js';
|
|
11
|
-
import { stream_to_buffer, inspect, output_width, assert } from './utils.js';
|
|
11
|
+
import { stream_to_buffer, inspect, output_width, assert, range_to_numbers, encode } from './utils.js';
|
|
12
12
|
import { flist, fread, fstat } from './file.js';
|
|
13
13
|
// ------------ my server
|
|
14
14
|
export class Server {
|
|
@@ -47,7 +47,6 @@ export class Server {
|
|
|
47
47
|
/** 原始 process.stdout.write 函数 bind 后的备份 */
|
|
48
48
|
stdout_write;
|
|
49
49
|
stderr_write;
|
|
50
|
-
encoder = new TextEncoder();
|
|
51
50
|
constructor({ name, http, http2, http_port, http2_port, fpd_certs, default_hostnames, remote, funcs, stdio_subscribable, }) {
|
|
52
51
|
this.name = name;
|
|
53
52
|
if (http)
|
|
@@ -155,7 +154,7 @@ export class Server {
|
|
|
155
154
|
// send 时有可能 websocket 连接断开,抛异常,为防止循环调用 (console.error -> stdio subscriber -> throw error -> console.error)
|
|
156
155
|
// 这里只能忽略错误
|
|
157
156
|
try {
|
|
158
|
-
await this.remote.send({ id, data: [typeof chunk === 'string' ?
|
|
157
|
+
await this.remote.send({ id, data: [typeof chunk === 'string' ? encode(chunk) : chunk] }, websocket);
|
|
159
158
|
}
|
|
160
159
|
catch { }
|
|
161
160
|
};
|
|
@@ -615,5 +614,30 @@ export class Server {
|
|
|
615
614
|
}
|
|
616
615
|
return fp;
|
|
617
616
|
}
|
|
617
|
+
/** - range: 取值为逗号分割的多个可用端口或端口区间 (不能含有空格,包含区间右值),比如:`8321,8322,8300-8310,11000-11999
|
|
618
|
+
- reverse?: `false` 在 range 内从后往前尝试 */
|
|
619
|
+
async get_available_port(range, reverse = false) {
|
|
620
|
+
for (const port of range_to_numbers(range, reverse)) {
|
|
621
|
+
let server = http_create_server();
|
|
622
|
+
try {
|
|
623
|
+
await new Promise((resolve, reject) => {
|
|
624
|
+
server.once('error', reject);
|
|
625
|
+
server.listen(port, resolve);
|
|
626
|
+
});
|
|
627
|
+
return port;
|
|
628
|
+
}
|
|
629
|
+
catch (error) {
|
|
630
|
+
if (error.code === 'EADDRINUSE')
|
|
631
|
+
console.log(`端口 ${port} 监听失败:${error.message}`);
|
|
632
|
+
else
|
|
633
|
+
throw error;
|
|
634
|
+
}
|
|
635
|
+
finally {
|
|
636
|
+
server.closeAllConnections();
|
|
637
|
+
server.close();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
throw new Error(`端口范围 {${range}} 内没有可用的端口`);
|
|
641
|
+
}
|
|
618
642
|
}
|
|
619
643
|
//# sourceMappingURL=server.js.map
|
package/utils.browser.d.ts
CHANGED
|
@@ -50,6 +50,11 @@ export declare class Lock<TResource = void> {
|
|
|
50
50
|
request<TResult>(action: LockedAction<TResource, TResult>, signal?: AbortSignal): Promise<TResult>;
|
|
51
51
|
}
|
|
52
52
|
export declare function pause(milliseconds?: number): Promise<void>;
|
|
53
|
+
/** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
|
|
54
|
+
export declare function encode(str: string): Uint8Array;
|
|
55
|
+
/** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
|
|
56
|
+
在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
|
|
57
|
+
export declare function decode(buffer: Uint8Array): string;
|
|
53
58
|
/** 字符串字典序比较 */
|
|
54
59
|
export declare function strcmp(l: string, r: string): 0 | 1 | -1;
|
|
55
60
|
/** 比较 1.10.02 这种版本号 */
|
package/utils.browser.js
CHANGED
|
@@ -122,6 +122,18 @@ export async function pause(milliseconds = 3000) {
|
|
|
122
122
|
debugger;
|
|
123
123
|
}
|
|
124
124
|
globalThis.pause = pause;
|
|
125
|
+
// ------------ text
|
|
126
|
+
let encoder = new TextEncoder();
|
|
127
|
+
/** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
|
|
128
|
+
export function encode(str) {
|
|
129
|
+
return encoder.encode(str);
|
|
130
|
+
}
|
|
131
|
+
let decoder = new TextDecoder();
|
|
132
|
+
/** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
|
|
133
|
+
在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
|
|
134
|
+
export function decode(buffer) {
|
|
135
|
+
return decoder.decode(buffer);
|
|
136
|
+
}
|
|
125
137
|
/** 字符串字典序比较 */
|
|
126
138
|
export function strcmp(l, r) {
|
|
127
139
|
if (l === r)
|
package/utils.d.ts
CHANGED
|
@@ -94,6 +94,11 @@ export declare class Lock<TResource = void> {
|
|
|
94
94
|
在 signal aborted 时抛出错误结束等待,且由内部实现自动释放资源 */
|
|
95
95
|
request<TResult>(action: LockedAction<TResource, TResult>, signal?: AbortSignal): Promise<TResult>;
|
|
96
96
|
}
|
|
97
|
+
/** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
|
|
98
|
+
export declare function encode(str: string): Uint8Array;
|
|
99
|
+
/** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
|
|
100
|
+
在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
|
|
101
|
+
export declare function decode(buffer: Uint8Array): string;
|
|
97
102
|
export declare function has_chinese(str: string): boolean;
|
|
98
103
|
export declare function escape_line_feed(str: string): string;
|
|
99
104
|
export declare function lowercase_first_letter(str: string): string;
|
|
@@ -143,3 +148,7 @@ export declare class DecoderStream extends Transform {
|
|
|
143
148
|
_transform(chunk: Uint8Array, encoding: BufferEncoding, callback: TransformCallback): void;
|
|
144
149
|
_flush(callback: TransformCallback): void;
|
|
145
150
|
}
|
|
151
|
+
/** 根据 range 生成整数序列 (iterable)
|
|
152
|
+
- range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
|
|
153
|
+
- reverse?: `false` 在 range 内从后往前生成 */
|
|
154
|
+
export declare function range_to_numbers(range: string, reverse?: boolean): Generator<number, void, unknown>;
|
package/utils.js
CHANGED
|
@@ -286,6 +286,17 @@ export class Lock {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
// ------------ text
|
|
289
|
+
let encoder = new TextEncoder();
|
|
290
|
+
/** 将字符串简单的编码为 utf-8 的 buffer (Uint8Array)。高频使用或者在流式处理时,考虑使用 TextEncoder 的 encodeInto 方法 */
|
|
291
|
+
export function encode(str) {
|
|
292
|
+
return encoder.encode(str);
|
|
293
|
+
}
|
|
294
|
+
let decoder = new TextDecoder();
|
|
295
|
+
/** 将 utf-8 buffer (Uint8Array) 简单的解码为 string。
|
|
296
|
+
在流式处理 (buffer 可能不完整) 时,应使用独立的 TextDecoder 实例调用 decode(buffer, { stream: true }) */
|
|
297
|
+
export function decode(buffer) {
|
|
298
|
+
return decoder.decode(buffer);
|
|
299
|
+
}
|
|
289
300
|
export function has_chinese(str) {
|
|
290
301
|
return /[\u4E00-\u9FA5]/.test(str);
|
|
291
302
|
}
|
|
@@ -499,4 +510,23 @@ export class DecoderStream extends Transform {
|
|
|
499
510
|
callback();
|
|
500
511
|
}
|
|
501
512
|
}
|
|
513
|
+
/** 根据 range 生成整数序列 (iterable)
|
|
514
|
+
- range: 取值为逗号分割的多个可用值或值区间 (不能含有空格),比如:`8321,8322,8300-8310,11000-11999`
|
|
515
|
+
- reverse?: `false` 在 range 内从后往前生成 */
|
|
516
|
+
export function* range_to_numbers(range, reverse = false) {
|
|
517
|
+
let parts = range.split(',');
|
|
518
|
+
if (reverse)
|
|
519
|
+
parts.reverse();
|
|
520
|
+
for (const part of parts) {
|
|
521
|
+
const [left, right] = part.split('-').map(n => Number(n));
|
|
522
|
+
if (!right)
|
|
523
|
+
yield left;
|
|
524
|
+
if (reverse)
|
|
525
|
+
for (let i = right; i >= left; i--)
|
|
526
|
+
yield i;
|
|
527
|
+
else
|
|
528
|
+
for (let i = left; i <= right; i++)
|
|
529
|
+
yield i;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
502
532
|
//# sourceMappingURL=utils.js.map
|
package/xshell.png
ADDED
|
Binary file
|