xshell 1.0.196 → 1.0.197
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/Terminal.d.ts +1 -0
- package/Terminal.js +5 -2
- package/apps.js +6 -6
- package/builder.js +9 -9
- package/file.js +27 -27
- package/net.js +12 -8
- package/package.json +11 -11
- package/server.js +10 -10
- package/xlint.js +2 -0
package/Terminal.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare function Terminal({ font }: {
|
|
|
7
7
|
font: string;
|
|
8
8
|
}): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
declare class TerminalModel extends Model<TerminalModel> {
|
|
10
|
+
pterm: import("./utils.browser.ts").Deferred<XTerminal>;
|
|
10
11
|
term: XTerminal;
|
|
11
12
|
fit_addon: FitAddon;
|
|
12
13
|
stdio_id: number;
|
package/Terminal.js
CHANGED
|
@@ -8,7 +8,7 @@ import { WebLinksAddon } from '@xterm/addon-web-links';
|
|
|
8
8
|
// 没有 ui
|
|
9
9
|
// import { SearchAddon } from '@xterm/addon-search'
|
|
10
10
|
import { Model } from 'react-object-model';
|
|
11
|
-
import { assert, genid } from './utils.browser.js';
|
|
11
|
+
import { assert, defer, genid } from './utils.browser.js';
|
|
12
12
|
export function Terminal({ font }) {
|
|
13
13
|
let rterminal = useRef();
|
|
14
14
|
useEffect(() => {
|
|
@@ -34,14 +34,17 @@ export function Terminal({ font }) {
|
|
|
34
34
|
term.loadAddon(link_addon);
|
|
35
35
|
// term.loadAddon(search_addon)
|
|
36
36
|
term.open(rterminal.current);
|
|
37
|
-
|
|
37
|
+
if (document.createElement('canvas').getContext('webgl2'))
|
|
38
|
+
term.loadAddon(new WebglAddon());
|
|
38
39
|
fit_addon.fit();
|
|
40
|
+
terminal.pterm.resolve(term);
|
|
39
41
|
terminal.set({ term, fit_addon });
|
|
40
42
|
})();
|
|
41
43
|
}, []);
|
|
42
44
|
return _jsx("div", { className: 'term', ref: rterminal });
|
|
43
45
|
}
|
|
44
46
|
class TerminalModel extends Model {
|
|
47
|
+
pterm = defer();
|
|
45
48
|
term;
|
|
46
49
|
fit_addon;
|
|
47
50
|
stdio_id = 0;
|
package/apps.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import util from 'util';
|
|
2
2
|
import { call_nodejs, platform, username } from './process.js';
|
|
3
|
-
import {
|
|
3
|
+
import { check } from './utils.js';
|
|
4
4
|
import { path } from './path.js';
|
|
5
5
|
export let npm = {
|
|
6
6
|
bin: platform == 'win32' ? `C:/Users/${username}/AppData/Roaming/npm/node_modules/pnpm/bin/pnpm.cjs` : '/usr/bin/pnpm',
|
|
@@ -68,11 +68,11 @@ export let oss = {
|
|
|
68
68
|
- cdn?: `false` false 时返回 oss 链接,true 时返回 cdn 链接
|
|
69
69
|
- copy?: `false` 是否复制到剪贴板 */
|
|
70
70
|
async upload(fp_remote, data, { private: _private = false, print = true, cdn = false, } = {}) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
check(this.client, 'OSS 应该已经初始化了');
|
|
72
|
+
check(!fp_remote.startsWith('/'), 'fp 不能以 / 开头');
|
|
73
|
+
check(!fp_remote.isdir, '不能使用 oss.upload 上传文件夹,请使用 oss.upload_dir');
|
|
74
74
|
if (typeof data === 'string')
|
|
75
|
-
|
|
75
|
+
check(path.isAbsolute(data), 'oss.upload 传入 data 参数类型为 string 时,必须为本地文件完整路径');
|
|
76
76
|
if (data instanceof Uint8Array && !Buffer.isBuffer(data))
|
|
77
77
|
data = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
78
78
|
await this.client.put(fp_remote, data);
|
|
@@ -89,7 +89,7 @@ export let oss = {
|
|
|
89
89
|
},
|
|
90
90
|
/** 获取经过签名后可访问的 url */
|
|
91
91
|
async get_url(fp) {
|
|
92
|
-
|
|
92
|
+
check(this.client, 'OSS 应该已经初始化了');
|
|
93
93
|
const url = new URL(this.client.signatureUrl(fp, { expires: /* 十年 */ 3600 * 24 * 365 * 10 }));
|
|
94
94
|
const url_ = `${this.fpd_cdn}${url.pathname.slice(1)}${url.search}`;
|
|
95
95
|
return url_;
|
package/builder.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fileURLToPath } from 'url';
|
|
2
2
|
import { noprint } from './process.js';
|
|
3
|
-
import { Lock,
|
|
3
|
+
import { Lock, check, filter_values, not_empty } from './utils.js';
|
|
4
4
|
import { fcopy, fmkdir, fwrite } from './file.js';
|
|
5
5
|
import { path } from './path.js';
|
|
6
6
|
const monaco_files = [
|
|
@@ -172,7 +172,7 @@ export class Bundler {
|
|
|
172
172
|
this.globals = globals;
|
|
173
173
|
this.exclude_modules = exclude_modules;
|
|
174
174
|
if (dependencies) {
|
|
175
|
-
|
|
175
|
+
check(target === 'web');
|
|
176
176
|
this.dependencies = dependencies;
|
|
177
177
|
}
|
|
178
178
|
if (expose && commonjs2)
|
|
@@ -307,6 +307,7 @@ export class Bundler {
|
|
|
307
307
|
// https://webpack.js.org/loaders/sass-loader
|
|
308
308
|
loader: get_loader('sass-loader'),
|
|
309
309
|
options: {
|
|
310
|
+
api: 'modern-compiler',
|
|
310
311
|
...sass ? {
|
|
311
312
|
implementation: sass
|
|
312
313
|
} : {},
|
|
@@ -393,7 +394,6 @@ export class Bundler {
|
|
|
393
394
|
};
|
|
394
395
|
}
|
|
395
396
|
async build(print = true) {
|
|
396
|
-
let timer = new Timer();
|
|
397
397
|
if (!this.lcompiler) {
|
|
398
398
|
const { default: Webpack } = await import('webpack');
|
|
399
399
|
this.config.plugins = [
|
|
@@ -447,8 +447,8 @@ export class Bundler {
|
|
|
447
447
|
if (print) {
|
|
448
448
|
const statstr = stats.toString(this.config.stats)
|
|
449
449
|
.replace(new RegExp(`\\n\\s*.*${this.name}.* compiled .*successfully.* in (.*)`), '').trimEnd();
|
|
450
|
-
console.log(
|
|
451
|
-
`${this.name}
|
|
450
|
+
console.log((statstr ? `${statstr}\n` : '') +
|
|
451
|
+
`${this.name} 成功构建到 ${this.fpd_out}`.green);
|
|
452
452
|
}
|
|
453
453
|
}
|
|
454
454
|
async close() {
|
|
@@ -512,8 +512,8 @@ export class Bundler {
|
|
|
512
512
|
if (print.files)
|
|
513
513
|
console.log(`已构建 ${fp_html}`);
|
|
514
514
|
}));
|
|
515
|
-
if (print.
|
|
516
|
-
console.log(`${this.name}
|
|
515
|
+
if (print.files)
|
|
516
|
+
console.log(`${this.name} 的所有 html 页面构建完成`);
|
|
517
517
|
}
|
|
518
518
|
resolve_dependencies(dependency_ids = this.dependencies, resolveds = new Set()) {
|
|
519
519
|
dependency_ids.forEach(id => {
|
|
@@ -541,8 +541,8 @@ export class Bundler {
|
|
|
541
541
|
}).flat();
|
|
542
542
|
}
|
|
543
543
|
async copy_files({ dependencies = this.dependencies, production = this.production, assets = this.assets, fpd_root = this.fpd_root, fpd_out = this.fpd_out, print = { info: true, files: false } } = {}) {
|
|
544
|
-
if (print.
|
|
545
|
-
console.log(`复制 ${this.name}
|
|
544
|
+
if (print.files)
|
|
545
|
+
console.log(`复制 ${this.name} 的依赖文件到 ${this.fpd_out}`);
|
|
546
546
|
if (dependencies.length)
|
|
547
547
|
await fmkdir(`${fpd_out}vendors/`, { print: false });
|
|
548
548
|
// 输出路径 -> 原路径,用来保证只复制一次且是同样的映射
|
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 {
|
|
6
|
+
import { check, Lock, WritableMemoryStream } from './utils.js';
|
|
7
7
|
export { fsp };
|
|
8
8
|
export const encodings = ['utf-8', 'gb18030', 'shift-jis', 'utf-16le'];
|
|
9
9
|
export const ramdisk = fexists('T:/TEMP/', { print: false });
|
|
@@ -31,12 +31,12 @@ export async function fopen(fp, flags = 'r', { mode, print } = {}) {
|
|
|
31
31
|
}
|
|
32
32
|
export async function fread(fp, { dir, encoding = 'utf-8', print = true } = {}) {
|
|
33
33
|
if (dir) {
|
|
34
|
-
|
|
34
|
+
check(dir.isdir, t('dir 必须以 / 结尾'));
|
|
35
35
|
fp = dir + fp;
|
|
36
36
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
check(!fp.isdir, t('fp 必须是文件,不能以 / 结尾'));
|
|
38
|
+
check(path.isAbsolute(fp), `${t('fp 必须是绝对路径:')} ${fp}`);
|
|
39
|
+
check(encoding !== 'auto');
|
|
40
40
|
if (print)
|
|
41
41
|
console.log(t('读取'), fp);
|
|
42
42
|
switch (encoding) {
|
|
@@ -82,7 +82,7 @@ export async function fwrite(fp, data, { print = true } = {}) {
|
|
|
82
82
|
console.log(t('写入'), fp.fp);
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
|
-
|
|
85
|
+
check(path.isAbsolute(fp), `${t('fp 必须是绝对路径,当前为:')} ${fp}`);
|
|
86
86
|
if (print)
|
|
87
87
|
console.log(t('写入'), fp);
|
|
88
88
|
}
|
|
@@ -103,10 +103,10 @@ export async function fwrite(fp, data, { print = true } = {}) {
|
|
|
103
103
|
}
|
|
104
104
|
/** 追加内容到文件末尾,如果文件不存在会自动创建 */
|
|
105
105
|
export async function fappend(fp, data, { print = true } = {}) {
|
|
106
|
-
|
|
106
|
+
check(path.isAbsolute(fp), `${t('fp 必须是绝对路径,当前为:')} ${fp}`);
|
|
107
107
|
if (print)
|
|
108
108
|
console.log(t('追加'), fp);
|
|
109
|
-
|
|
109
|
+
check(isUint8Array(data) || typeof data === 'string');
|
|
110
110
|
try {
|
|
111
111
|
await fsp.appendFile(fp, data);
|
|
112
112
|
}
|
|
@@ -121,8 +121,8 @@ export async function fappend(fp, data, { print = true } = {}) {
|
|
|
121
121
|
}
|
|
122
122
|
export async function flist(fpd, options = {}) {
|
|
123
123
|
const { filter, deep = false, absolute = false, print = true, stats = false } = options;
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
check(path.isAbsolute(fpd), t("flist: 参数 fpd: '{{fpd}}' 必须是绝对路径", { fpd }));
|
|
125
|
+
check(fpd.isdir, t("flist: 参数 fpd: '{{fpd}}' 必须以 / 结尾", { fpd }));
|
|
126
126
|
if (!path.isAbsolute(fpd))
|
|
127
127
|
throw new Error(t('参数 fpd: ') + fpd + t(' 必须是绝对路径'));
|
|
128
128
|
if (!fpd.isdir)
|
|
@@ -179,13 +179,13 @@ export async function flist(fpd, options = {}) {
|
|
|
179
179
|
return fps;
|
|
180
180
|
}
|
|
181
181
|
export async function fstat(fp) {
|
|
182
|
-
|
|
182
|
+
check(path.isAbsolute(fp), t("fstat: 参数 fp: '{{fp}}' 必须是绝对路径", { fp }));
|
|
183
183
|
let stat = await fsp.stat(fp, { bigint: true });
|
|
184
184
|
stat.fp = fp;
|
|
185
185
|
return stat;
|
|
186
186
|
}
|
|
187
187
|
export async function flstat(fp) {
|
|
188
|
-
|
|
188
|
+
check(path.isAbsolute(fp), t("flstat: 参数 fp: '{{fp}}' 必须是绝对路径", { fp }));
|
|
189
189
|
let stat = await fsp.lstat(fp, { bigint: true });
|
|
190
190
|
stat.fp = fp;
|
|
191
191
|
return stat;
|
|
@@ -205,8 +205,8 @@ export async function ffstat(handle) {
|
|
|
205
205
|
- options?:
|
|
206
206
|
- print?: `true` */
|
|
207
207
|
export async function fdelete(fp, { print = true } = {}) {
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
check(fp.length >= 6, `fp: ${fp} ${t('不能太短,防止误删文件')}`);
|
|
209
|
+
check(path.isAbsolute(fp), t('fp 必须是绝对路径'));
|
|
210
210
|
const { isdir } = fp;
|
|
211
211
|
try {
|
|
212
212
|
await fsp.rm(fp, { recursive: true });
|
|
@@ -248,8 +248,8 @@ export async function fdclear(fpd, { print = true } = {}) {
|
|
|
248
248
|
fcopy('D:/temp/camera/', 'D:/camera/') */
|
|
249
249
|
export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, filter, } = {}) {
|
|
250
250
|
const { isdir } = fp_src;
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
check(isdir === fp_dst.isdir, t('fp_src 和 fp_dst 必须同为文件路径或文件夹路径'));
|
|
252
|
+
check(path.isAbsolute(fp_src) && path.isAbsolute(fp_dst), t('fp_src 和 fp_dst 必须为完整路径'));
|
|
253
253
|
if (!isdir && filter)
|
|
254
254
|
throw new Error(t('filter 选项只适用于 fp_src 为文件夹'));
|
|
255
255
|
if (print)
|
|
@@ -297,8 +297,8 @@ export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, fi
|
|
|
297
297
|
@example
|
|
298
298
|
fmove('D:/temp/camera/', 'D:/camera/') */
|
|
299
299
|
export async function fmove(fp_src, fp_dst, { overwrite = false, print = true } = {}) {
|
|
300
|
-
|
|
301
|
-
|
|
300
|
+
check(fp_src.isdir === fp_dst.isdir, t('fp_src 和 fp_dst 必须同为文件路径或文件夹路径'));
|
|
301
|
+
check(path.isAbsolute(fp_src) && path.isAbsolute(fp_dst), t('fp_src 和 fp_dst 必须为完整路径'));
|
|
302
302
|
if (print)
|
|
303
303
|
console.log(t('移动'), fp_src, '->', fp_dst);
|
|
304
304
|
if (!overwrite && fexists(fp_dst, { print: false }))
|
|
@@ -330,11 +330,11 @@ export async function fmove(fp_src, fp_dst, { overwrite = false, print = true }
|
|
|
330
330
|
- overwrite?: `true` 默认覆盖(不检查效率更高) better performance without check */
|
|
331
331
|
export async function frename(fp, fp_, { fpd, print = true, overwrite = true } = {}) {
|
|
332
332
|
if (fpd) {
|
|
333
|
-
|
|
333
|
+
check(fpd.isdir);
|
|
334
334
|
fp = fpd + fp;
|
|
335
335
|
fp_ = fpd + fp_;
|
|
336
336
|
}
|
|
337
|
-
|
|
337
|
+
check(path.isAbsolute(fp) && path.isAbsolute(fp_), t('fp 和 fp_ 必须是绝对路径'));
|
|
338
338
|
if (print)
|
|
339
339
|
console.log(t('重命名'), fp, '->', fp_);
|
|
340
340
|
if (!overwrite && fexists(fp_, { print: false }))
|
|
@@ -350,8 +350,8 @@ export async function frename(fp, fp_, { fpd, print = true, overwrite = true } =
|
|
|
350
350
|
- print?: `true`
|
|
351
351
|
- mode?: `'0o777'` */
|
|
352
352
|
export async function fmkdir(fpd, { print = true, mode, } = {}) {
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
check(path.isAbsolute(fpd), t('fpd 必须是绝对路径: ') + fpd);
|
|
354
|
+
check(fpd.isdir, t('fpd 必须以 / 结尾: ') + fpd);
|
|
355
355
|
// 类似 D:/ 这样的根路径,调用 fsp.mkdir 会报错无权限
|
|
356
356
|
if (fpd.length === 3 &&
|
|
357
357
|
'A' <= fpd[0] && fpd[0] <= 'Z' && fpd[1] === ':' && fpd[2] === '/')
|
|
@@ -376,10 +376,10 @@ export async function fmkdir(fpd, { print = true, mode, } = {}) {
|
|
|
376
376
|
- fp_real: 现在真实文件/文件夹的路径
|
|
377
377
|
- fp_link: 目标链接文件/文件夹的路径 */
|
|
378
378
|
export async function flink(fp_real, fp_link, { junction = false, print = true, skip_existing = false } = {}) {
|
|
379
|
-
|
|
379
|
+
check(path.isAbsolute(fp_real) && path.isAbsolute(fp_link), 'fp 必须是绝对路径');
|
|
380
380
|
const is_fpd_real = fp_real.isdir;
|
|
381
381
|
const is_fpd_link = fp_link.isdir;
|
|
382
|
-
|
|
382
|
+
check(is_fpd_real === is_fpd_link, 'fp_real 和 fp_link 必须同为文件路径或文件夹路径');
|
|
383
383
|
if (fexists(fp_link, { print: false }))
|
|
384
384
|
if (!skip_existing)
|
|
385
385
|
throw new Error(`存在同名${is_fpd_link ? '文件夹' : '文件'}: ${fp_link},无法创建链接`);
|
|
@@ -434,7 +434,7 @@ async function _zip(data, fp_zip, { dirname, print = { files: true, info: true }
|
|
|
434
434
|
.map(fp => ([dirname + fp, fpd_src + fp])));
|
|
435
435
|
}
|
|
436
436
|
else {
|
|
437
|
-
|
|
437
|
+
check(!dirname, 'dirname 在传入 fpd_src 时才生效');
|
|
438
438
|
entries = data;
|
|
439
439
|
}
|
|
440
440
|
if (print.info)
|
|
@@ -463,8 +463,8 @@ async function _zip(data, fp_zip, { dirname, print = { files: true, info: true }
|
|
|
463
463
|
archive.append(Buffer.isBuffer(fdata) ? fdata : Buffer.from(fdata.buffer), { name: fp });
|
|
464
464
|
}
|
|
465
465
|
else {
|
|
466
|
-
|
|
467
|
-
|
|
466
|
+
check(fp.isdir === fdata.isdir);
|
|
467
|
+
check(path.isAbsolute(fdata));
|
|
468
468
|
if (print.files)
|
|
469
469
|
console.log(`压缩 ${fdata} -> ${fp}`);
|
|
470
470
|
if (fp.isdir)
|
package/net.js
CHANGED
|
@@ -38,13 +38,17 @@ const drop_request_headers = new Set([
|
|
|
38
38
|
'upgrade',
|
|
39
39
|
]);
|
|
40
40
|
let proxy_agents = {};
|
|
41
|
-
async function request_retry(url, options,
|
|
41
|
+
async function request_retry(url, options, _timeout, retries = 0, count = 0) {
|
|
42
42
|
let { default: undici, request: undici_request } = await import('undici');
|
|
43
43
|
undici_request ??= undici.request;
|
|
44
44
|
try {
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
if (_timeout > 0) {
|
|
46
|
+
// 设置给 undici 设置 timeout, signal 不一定管用,还是得自己兜底
|
|
47
|
+
options.signal = AbortSignal.timeout(_timeout);
|
|
48
|
+
return await timeout(_timeout + 300 /* 为 undici 兜底 */, async () => undici.request(url, options));
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
return await undici.request(url, options);
|
|
48
52
|
}
|
|
49
53
|
catch (error) {
|
|
50
54
|
if (error.name === 'TimeoutError')
|
|
@@ -52,7 +56,7 @@ async function request_retry(url, options, timeout, retries = 0, count = 0) {
|
|
|
52
56
|
const duration = 2 ** count;
|
|
53
57
|
console.log(`${t('等待 {{duration}} 秒后重试 request (已尝试 {{_count}} 次) …', { duration, _count: count + 1 }).yellow} ${url.toString().blue.underline}`);
|
|
54
58
|
await delay(1000 * duration);
|
|
55
|
-
return request_retry(url, options,
|
|
59
|
+
return request_retry(url, options, _timeout, retries, count + 1);
|
|
56
60
|
}
|
|
57
61
|
else
|
|
58
62
|
throw Object.assign(new Error(`request 超时: ${url.toString()}`), { name: 'TimeoutError' });
|
|
@@ -127,11 +131,11 @@ export async function request(url, options = {}) {
|
|
|
127
131
|
// todo: 强制手动处理重定向,来正确处理 cookie ?
|
|
128
132
|
maxRedirections: redirect === 'follow' ? 5 : 0,
|
|
129
133
|
// 下面这些 timeout 都不是总的时间,没法用
|
|
130
|
-
|
|
134
|
+
headersTimeout: timeout,
|
|
131
135
|
// 从收完 headers 开始算
|
|
132
|
-
|
|
136
|
+
bodyTimeout: timeout,
|
|
133
137
|
// @ts-ignore 没有类型声明,实际可用
|
|
134
|
-
|
|
138
|
+
connectTimeout: timeout,
|
|
135
139
|
headers,
|
|
136
140
|
// --- body
|
|
137
141
|
body: (() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.197",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"bin": {
|
|
@@ -53,13 +53,13 @@
|
|
|
53
53
|
"@babel/parser": "^7.26.2",
|
|
54
54
|
"@babel/traverse": "^7.25.9",
|
|
55
55
|
"@koa/cors": "^5.0.0",
|
|
56
|
-
"@stylistic/eslint-plugin": "^2.
|
|
56
|
+
"@stylistic/eslint-plugin": "^2.11.0",
|
|
57
57
|
"@svgr/webpack": "^8.1.0",
|
|
58
58
|
"@types/sass-loader": "^8.0.9",
|
|
59
59
|
"@types/ws": "^8.5.13",
|
|
60
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
61
|
-
"@typescript-eslint/parser": "^8.
|
|
62
|
-
"@typescript-eslint/utils": "^8.
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^8.15.0",
|
|
61
|
+
"@typescript-eslint/parser": "^8.15.0",
|
|
62
|
+
"@typescript-eslint/utils": "^8.15.0",
|
|
63
63
|
"@xterm/addon-fit": "^0.10.0",
|
|
64
64
|
"@xterm/addon-web-links": "^0.11.0",
|
|
65
65
|
"@xterm/addon-webgl": "^0.18.0",
|
|
@@ -75,13 +75,13 @@
|
|
|
75
75
|
"commander": "^12.1.0",
|
|
76
76
|
"css-loader": "^7.1.2",
|
|
77
77
|
"emoji-regex": "^10.4.0",
|
|
78
|
-
"eslint": "^9.
|
|
78
|
+
"eslint": "^9.15.0",
|
|
79
79
|
"eslint-plugin-import": "^2.31.0",
|
|
80
80
|
"eslint-plugin-react": "^7.37.2",
|
|
81
81
|
"gulp-sort": "^2.0.0",
|
|
82
82
|
"hash-string": "^1.0.0",
|
|
83
83
|
"https-proxy-agent": "^7.0.5",
|
|
84
|
-
"i18next": "^23.16.
|
|
84
|
+
"i18next": "^23.16.5",
|
|
85
85
|
"i18next-scanner": "^4.6.0",
|
|
86
86
|
"koa": "^2.15.3",
|
|
87
87
|
"koa-compress": "^5.1.1",
|
|
@@ -91,10 +91,10 @@
|
|
|
91
91
|
"mime-types": "^2.1.35",
|
|
92
92
|
"ora": "^8.1.1",
|
|
93
93
|
"react": "^18.3.1",
|
|
94
|
-
"react-i18next": "^15.1.
|
|
94
|
+
"react-i18next": "^15.1.1",
|
|
95
95
|
"react-object-model": "^1.2.17",
|
|
96
96
|
"resolve-path": "^1.4.0",
|
|
97
|
-
"sass": "^1.
|
|
97
|
+
"sass": "^1.81.0",
|
|
98
98
|
"sass-loader": "^16.0.3",
|
|
99
99
|
"source-map-loader": "^5.0.0",
|
|
100
100
|
"strip-ansi": "^7.1.0",
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
"ts-loader": "^9.5.1",
|
|
105
105
|
"tslib": "^2.8.1",
|
|
106
106
|
"typescript": "^5.6.3",
|
|
107
|
-
"ua-parser-js": "^2.0.0-rc.
|
|
108
|
-
"undici": "^6.
|
|
107
|
+
"ua-parser-js": "^2.0.0-rc.3",
|
|
108
|
+
"undici": "^6.21.0",
|
|
109
109
|
"vinyl": "^3.0.0",
|
|
110
110
|
"vinyl-fs": "^4.0.0",
|
|
111
111
|
"webpack": "^5.96.1",
|
package/server.js
CHANGED
|
@@ -2,13 +2,13 @@ import { createServer as http_create_server, } from 'http';
|
|
|
2
2
|
import { createSecureServer as http2_create_server } from 'http2';
|
|
3
3
|
import { createSecureContext } from 'tls';
|
|
4
4
|
import zlib from 'zlib';
|
|
5
|
-
import
|
|
5
|
+
import fs from 'fs';
|
|
6
6
|
import { buffer as stream_to_buffer } from 'stream/consumers';
|
|
7
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 { inspect, output_width,
|
|
11
|
+
import { inspect, output_width, check, range_to_numbers, encode, filter_keys, filter_values, consume_stream, assert } from './utils.js';
|
|
12
12
|
import { flist, fread, fstat } from './file.js';
|
|
13
13
|
// ------------ my server
|
|
14
14
|
export class Server {
|
|
@@ -57,7 +57,7 @@ export class Server {
|
|
|
57
57
|
this.http = http;
|
|
58
58
|
if (http2)
|
|
59
59
|
this.http2 = http2;
|
|
60
|
-
|
|
60
|
+
check(this.http || this.http2, 'http 和 http2 至少启用一个');
|
|
61
61
|
if (http_port !== undefined)
|
|
62
62
|
this.http_port = http_port;
|
|
63
63
|
if (http2_port !== undefined)
|
|
@@ -73,7 +73,7 @@ export class Server {
|
|
|
73
73
|
else if (funcs)
|
|
74
74
|
this.remote = new Remote({ funcs });
|
|
75
75
|
if (stdio_subscribable !== undefined) {
|
|
76
|
-
|
|
76
|
+
check(remote || funcs);
|
|
77
77
|
this.stdio_subscribable = stdio_subscribable;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -125,7 +125,7 @@ export class Server {
|
|
|
125
125
|
return [domain, { key, cert }];
|
|
126
126
|
})));
|
|
127
127
|
const default_ctx = lazy_secure_ctxs[this.default_hostnames.find(hostname => hostname in lazy_secure_ctxs)];
|
|
128
|
-
|
|
128
|
+
check(default_ctx);
|
|
129
129
|
this.http2_server = http2_create_server({
|
|
130
130
|
SNICallback(servername, callback) {
|
|
131
131
|
let lazy_ctx = lazy_secure_ctxs[servername] ||
|
|
@@ -571,7 +571,7 @@ export class Server {
|
|
|
571
571
|
- download?: `undefined` 在 response.headers 中加上 content-disposition: attachment 指示浏览器下载文件
|
|
572
572
|
- assets_root?: 替换模板 xxx.template.html 中 {root} 占位符 */
|
|
573
573
|
async fsend(ctx, fp, { root, absolute, download, assets_root, } = {}) {
|
|
574
|
-
|
|
574
|
+
check(absolute || root?.isdir, t('fsend 必须传 absolute 选项或 root 文件夹'));
|
|
575
575
|
const { request, request: { method } } = ctx;
|
|
576
576
|
let { response } = ctx;
|
|
577
577
|
if (!absolute) {
|
|
@@ -630,7 +630,7 @@ export class Server {
|
|
|
630
630
|
return fp;
|
|
631
631
|
}
|
|
632
632
|
if (assets_root) {
|
|
633
|
-
|
|
633
|
+
check(fp.endsWith('.html'), '传入 assets_root 参数时 fp 应该为 .html 文件');
|
|
634
634
|
response.body = (await fread(fp, { print: false }))
|
|
635
635
|
.replaceAll('{root}', assets_root);
|
|
636
636
|
return fp;
|
|
@@ -651,17 +651,17 @@ export class Server {
|
|
|
651
651
|
response.status = 206;
|
|
652
652
|
response.set('content-length', String(chunksize));
|
|
653
653
|
response.set('content-range', `bytes ${start}-${end}/${stats.size}`);
|
|
654
|
-
response.body = createReadStream(fp, { start, end });
|
|
654
|
+
response.body = fs.createReadStream(fp, { start, end });
|
|
655
655
|
}
|
|
656
656
|
catch {
|
|
657
657
|
response.status = 416;
|
|
658
658
|
response.set('content-length', String(stats.size));
|
|
659
659
|
response.set('content-range', `bytes */${stats.size}`);
|
|
660
|
-
response.body = createReadStream(fp);
|
|
660
|
+
response.body = fs.createReadStream(fp);
|
|
661
661
|
}
|
|
662
662
|
else {
|
|
663
663
|
response.set('content-length', String(stats.size));
|
|
664
|
-
response.body = createReadStream(fp);
|
|
664
|
+
response.body = fs.createReadStream(fp);
|
|
665
665
|
}
|
|
666
666
|
return fp;
|
|
667
667
|
}
|
package/xlint.js
CHANGED
|
@@ -795,6 +795,8 @@ export const xlint_config = {
|
|
|
795
795
|
// ------------ 括号
|
|
796
796
|
// a => { } 而不是 (a) => { }
|
|
797
797
|
'@stylistic/arrow-parens': ['error', 'as-needed', { requireForBlockBody: false }],
|
|
798
|
+
// arrow function 在 return 单条表达式时去掉后面的大括号, a => 1
|
|
799
|
+
'arrow-body-style': 'error',
|
|
798
800
|
// 不要多余的大括号
|
|
799
801
|
// if (true)
|
|
800
802
|
// console.log()
|