xshell 1.0.49 → 1.0.50
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 +8 -1
- package/file.js +66 -2
- package/i18n/dict.json +3 -0
- package/net.js +4 -2
- package/package.json +8 -8
- package/utils.js +2 -2
package/file.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare function fexists(fp: string, { print }?: {
|
|
|
21
21
|
- flags: `'r'`
|
|
22
22
|
- options?:
|
|
23
23
|
- mode?: `'0o666'` Sets the file mode (permission and sticky bits) if the file is created. */
|
|
24
|
-
export declare function fopen(fp: string, flags
|
|
24
|
+
export declare function fopen(fp: string, flags?: string | number, { mode, print }?: {
|
|
25
25
|
mode?: fs.Mode;
|
|
26
26
|
print?: boolean;
|
|
27
27
|
}): Promise<fsp.FileHandle & {
|
|
@@ -91,6 +91,8 @@ export declare function flist(fpd: string, options?: FListOptions & {
|
|
|
91
91
|
}): Promise<FStats[]>;
|
|
92
92
|
export declare function flist(fpd: string, options?: FListOptions): Promise<string[]>;
|
|
93
93
|
export declare function fstat(fp: string): Promise<FStats>;
|
|
94
|
+
export declare function flstat(fp: string): Promise<FStats>;
|
|
95
|
+
export declare function ffstat(handle: fsp.FileHandle): Promise<fs.BigIntStats>;
|
|
94
96
|
/** 删除文件或文件夹 delete files or folders
|
|
95
97
|
- fp: 文件或文件夹的完整路径 The full path to the file or folder
|
|
96
98
|
- options?:
|
|
@@ -155,6 +157,11 @@ export declare function flink(fp_real: string, fp_link: string, { junction, prin
|
|
|
155
157
|
junction?: boolean;
|
|
156
158
|
print?: boolean;
|
|
157
159
|
}): Promise<void>;
|
|
160
|
+
export declare let fwatchers: Record<string, fs.FSWatcher>;
|
|
161
|
+
/** 跟踪文本文件追加的内容,类似 tail -f */
|
|
162
|
+
export declare function ftail(fp: string, handler: (lines: string[]) => void | Promise<void>, { print }?: {
|
|
163
|
+
print?: boolean;
|
|
164
|
+
}): Promise<fs.FSWatcher>;
|
|
158
165
|
/** 打开一个文件并搜索替换某个 pattern */
|
|
159
166
|
export declare function freplace(fp: string, pattern: string | RegExp, replacement: string, { print }?: {
|
|
160
167
|
print?: boolean;
|
package/file.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isUint8Array } from 'util/types';
|
|
|
3
3
|
import { path } from './path.js';
|
|
4
4
|
import { t } from './i18n/instance.js';
|
|
5
5
|
import { to_json } from './prototype.js';
|
|
6
|
-
import { assert } from './utils.js';
|
|
6
|
+
import { assert, Lock } from './utils.js';
|
|
7
7
|
export const encodings = ['utf-8', 'gb18030', 'shift-jis', 'utf-16le'];
|
|
8
8
|
/** fp 所指向的 文件/ 文件夹 是否存在
|
|
9
9
|
Does the file/folder pointed to by fp exist? */
|
|
@@ -22,7 +22,7 @@ export function fexists(fp, { print = true } = {}) {
|
|
|
22
22
|
- flags: `'r'`
|
|
23
23
|
- options?:
|
|
24
24
|
- mode?: `'0o666'` Sets the file mode (permission and sticky bits) if the file is created. */
|
|
25
|
-
export async function fopen(fp, flags, { mode, print } = {}) {
|
|
25
|
+
export async function fopen(fp, flags = 'r', { mode, print } = {}) {
|
|
26
26
|
if (print)
|
|
27
27
|
console.log(t('打开文件'), fp);
|
|
28
28
|
return Object.assign(await fsp.open(fp, flags, mode), { fp, flags, mode });
|
|
@@ -163,6 +163,22 @@ export async function fstat(fp) {
|
|
|
163
163
|
stat.fp = fp;
|
|
164
164
|
return stat;
|
|
165
165
|
}
|
|
166
|
+
export async function flstat(fp) {
|
|
167
|
+
assert(path.isAbsolute(fp), t("flstat: 参数 fp: '{{fp}}' 必须是绝对路径", { fp }));
|
|
168
|
+
let stat = await fsp.lstat(fp, { bigint: true });
|
|
169
|
+
stat.fp = fp;
|
|
170
|
+
return stat;
|
|
171
|
+
}
|
|
172
|
+
export async function ffstat(handle) {
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
fs.fstat(handle.fd, { bigint: true }, (error, stats) => {
|
|
175
|
+
if (error)
|
|
176
|
+
reject(error);
|
|
177
|
+
else
|
|
178
|
+
resolve(stats);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
166
182
|
/** 删除文件或文件夹 delete files or folders
|
|
167
183
|
- fp: 文件或文件夹的完整路径 The full path to the file or folder
|
|
168
184
|
- options?:
|
|
@@ -294,6 +310,54 @@ export async function flink(fp_real, fp_link, { junction = false, print = true }
|
|
|
294
310
|
else
|
|
295
311
|
fsp.symlink(fp_real, fp_link, is_fpd_real ? 'dir' : 'file');
|
|
296
312
|
}
|
|
313
|
+
export let fwatchers = {};
|
|
314
|
+
/** 跟踪文本文件追加的内容,类似 tail -f */
|
|
315
|
+
export async function ftail(fp, handler, { print = true } = {}) {
|
|
316
|
+
fwatchers[fp]?.close();
|
|
317
|
+
const { size } = await fstat(fp);
|
|
318
|
+
let pointer = Number(size);
|
|
319
|
+
let lock = new Lock(await fopen(fp));
|
|
320
|
+
let fbuf = new Uint8Array(2 ** 20);
|
|
321
|
+
let strbuf = '';
|
|
322
|
+
let decoder = new TextDecoder();
|
|
323
|
+
if (print)
|
|
324
|
+
console.log('开始跟踪追加内容', fp);
|
|
325
|
+
const { default: throttle } = await import('lodash/throttle.js');
|
|
326
|
+
const onchange_throttled = throttle(async () => {
|
|
327
|
+
if (lock.locked)
|
|
328
|
+
return;
|
|
329
|
+
await lock.request(async (handle) => {
|
|
330
|
+
const { bytesRead } = await handle.read(fbuf, 0, fbuf.length, pointer);
|
|
331
|
+
pointer += bytesRead;
|
|
332
|
+
const chunk = decoder.decode(fbuf.subarray(0, bytesRead), { stream: true });
|
|
333
|
+
let lines = [];
|
|
334
|
+
let j = 0;
|
|
335
|
+
for (let i = 0; (i = chunk.indexOf('\n', j)) >= 0;) {
|
|
336
|
+
let line = chunk.slice(j, i);
|
|
337
|
+
if (strbuf) {
|
|
338
|
+
line = strbuf + line;
|
|
339
|
+
strbuf = '';
|
|
340
|
+
}
|
|
341
|
+
j = i + 1;
|
|
342
|
+
lines.push(line);
|
|
343
|
+
}
|
|
344
|
+
strbuf = chunk.slice(j);
|
|
345
|
+
await handler(lines);
|
|
346
|
+
});
|
|
347
|
+
}, 250);
|
|
348
|
+
let watcher = fs.watch(fp, event => {
|
|
349
|
+
if (event === 'change')
|
|
350
|
+
onchange_throttled();
|
|
351
|
+
else {
|
|
352
|
+
console.error(`被监听的文件 ${fp.quote()} 出现了 rename 事件,结束 ftail`);
|
|
353
|
+
watcher.close();
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
watcher.on('error', error => {
|
|
357
|
+
console.error(error);
|
|
358
|
+
});
|
|
359
|
+
return fwatchers[fp] = watcher;
|
|
360
|
+
}
|
|
297
361
|
/** 打开一个文件并搜索替换某个 pattern */
|
|
298
362
|
export async function freplace(fp, pattern, replacement, { print = true } = {}) {
|
|
299
363
|
await fwrite(fp, (await fread(fp, { print }))
|
package/i18n/dict.json
CHANGED
|
@@ -343,5 +343,8 @@
|
|
|
343
343
|
},
|
|
344
344
|
"xshell 启动成功,正在监听: http://localhost:8421": {
|
|
345
345
|
"en": "xshell started successfully and is listening: http://localhost:8421"
|
|
346
|
+
},
|
|
347
|
+
"flstat: 参数 fp: '{{fp}}' 必须是绝对路径": {
|
|
348
|
+
"en": "flstat: parameter fp: '{{fp}}' must be an absolute path"
|
|
346
349
|
}
|
|
347
350
|
}
|
package/net.js
CHANGED
|
@@ -87,7 +87,9 @@ export async function request(url, { method, queries, headers: _headers, body, t
|
|
|
87
87
|
// --- headers, http/2 开始都用小写的 headers
|
|
88
88
|
let headers = new Headers({
|
|
89
89
|
'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja-JP;q=0.6,ja;q=0.5',
|
|
90
|
-
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
90
|
+
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36',
|
|
91
|
+
'sec-ch-ua-platform': '"Windows"',
|
|
92
|
+
'sec-ch-ua-platform-version': '"15.0.0"',
|
|
91
93
|
});
|
|
92
94
|
if (body !== undefined)
|
|
93
95
|
headers.set('content-type', type);
|
|
@@ -541,7 +543,7 @@ export class Remote {
|
|
|
541
543
|
作为 websocket 连接接收方,必传 websocket 参数
|
|
542
544
|
发送或连接出错时自动清理 message.id 对应的 handler */
|
|
543
545
|
async send(message, websocket) {
|
|
544
|
-
assert(!message.data || message.data.every(arg => arg !== undefined), 'message.data 数组中不能有 undefined 的项, 因为 json 序列化后会变为 null');
|
|
546
|
+
assert(!message.data || message.data.every(arg => arg !== undefined), t('message.data 数组中不能有 undefined 的项, 因为 json 序列化后会变为 null'));
|
|
545
547
|
if (this.print)
|
|
546
548
|
console.log('remote.send:', message);
|
|
547
549
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.50",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"interactive programming"
|
|
17
17
|
],
|
|
18
18
|
"engines": {
|
|
19
|
-
"node": ">=20.5.
|
|
19
|
+
"node": ">=20.5.1",
|
|
20
20
|
"vscode": ">=1.79.0"
|
|
21
21
|
},
|
|
22
22
|
"author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"strip-ansi": "^7.1.0",
|
|
78
78
|
"through2": "^4.0.2",
|
|
79
79
|
"tough-cookie": "^4.1.3",
|
|
80
|
-
"tslib": "^2.6.
|
|
80
|
+
"tslib": "^2.6.2",
|
|
81
81
|
"typescript": "^5.1.6",
|
|
82
82
|
"ua-parser-js": "2.0.0-alpha.2",
|
|
83
83
|
"undici": "^5.23.0",
|
|
@@ -97,18 +97,18 @@
|
|
|
97
97
|
"@types/koa-compress": "^4.0.3",
|
|
98
98
|
"@types/lodash": "^4.14.197",
|
|
99
99
|
"@types/mime-types": "^2.1.1",
|
|
100
|
-
"@types/node": "^20.
|
|
100
|
+
"@types/node": "^20.5.1",
|
|
101
101
|
"@types/react": "^18.2.20",
|
|
102
102
|
"@types/through2": "^2.0.38",
|
|
103
103
|
"@types/tough-cookie": "^4.0.2",
|
|
104
104
|
"@types/ua-parser-js": "^0.7.36",
|
|
105
105
|
"@types/vinyl-fs": "^3.0.2",
|
|
106
106
|
"@types/vscode": "^1.81.0",
|
|
107
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
108
|
-
"@typescript-eslint/parser": "^6.
|
|
107
|
+
"@typescript-eslint/eslint-plugin": "^6.4.0",
|
|
108
|
+
"@typescript-eslint/parser": "^6.4.0",
|
|
109
109
|
"eslint": "^8.47.0",
|
|
110
|
-
"eslint-plugin-react": "^7.33.
|
|
111
|
-
"eslint-plugin-xlint": "^1.0.
|
|
110
|
+
"eslint-plugin-react": "^7.33.2",
|
|
111
|
+
"eslint-plugin-xlint": "^1.0.7"
|
|
112
112
|
},
|
|
113
113
|
"scripts": {
|
|
114
114
|
"start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
|
package/utils.js
CHANGED
|
@@ -405,8 +405,8 @@ export async function stream_to_buffer(stream) {
|
|
|
405
405
|
export async function* stream_to_lines(stream) {
|
|
406
406
|
let buf = '';
|
|
407
407
|
for await (const chunk of stream) {
|
|
408
|
-
let
|
|
409
|
-
for (; (i = chunk.indexOf('\n', j)) >= 0;) {
|
|
408
|
+
let j = 0;
|
|
409
|
+
for (let i = 0; (i = chunk.indexOf('\n', j)) >= 0;) {
|
|
410
410
|
let line = chunk.slice(j, i);
|
|
411
411
|
if (buf) {
|
|
412
412
|
line = buf + line;
|