xshell 1.0.59 → 1.0.61
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/.eslintrc.json +12 -1
- package/Terminal.d.ts +15 -0
- package/Terminal.js +81 -0
- package/file.d.ts +7 -5
- package/file.js +25 -15
- package/i18n/dict.json +3 -0
- package/i18n/scanner/index.js +10 -10
- package/net.js +1 -1
- package/package.json +41 -36
- package/patches/{@types__byte-size@8.1.0.patch → @types__byte-size@8.1.2.patch} +3 -3
- package/process.d.ts +12 -7
- package/process.js +13 -7
- package/prototype.browser.js +1 -1
- package/server.d.ts +14 -0
- package/server.js +53 -12
- package/utils.browser.d.ts +4 -0
- package/utils.browser.js +20 -0
- package/utils.d.ts +4 -0
- package/utils.js +20 -0
package/.eslintrc.json
CHANGED
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
|
|
52
52
|
"xlint/keep-indent": "error",
|
|
53
53
|
|
|
54
|
+
// 函数使用 function 来声明而不是 const foo = () => { }
|
|
55
|
+
"xlint/func-style": "error",
|
|
56
|
+
|
|
54
57
|
|
|
55
58
|
"@typescript-eslint/semi": ["error", "never"],
|
|
56
59
|
"@typescript-eslint/no-extra-semi": "error",
|
|
@@ -170,6 +173,14 @@
|
|
|
170
173
|
|
|
171
174
|
"@typescript-eslint/prefer-includes": "error",
|
|
172
175
|
|
|
173
|
-
"@typescript-eslint/prefer-regexp-exec": "error"
|
|
176
|
+
"@typescript-eslint/prefer-regexp-exec": "error",
|
|
177
|
+
|
|
178
|
+
// 禁止使用 export default
|
|
179
|
+
"no-restricted-exports": ["error", { "restrictDefaultExports": { "direct": true, "named": true, "defaultFrom": true, "namedFrom": true, "namespaceFrom": true } }],
|
|
180
|
+
|
|
181
|
+
"@typescript-eslint/consistent-type-imports": ["error", { "fixStyle": "inline-type-imports", "disallowTypeAnnotations": false }],
|
|
182
|
+
|
|
183
|
+
// () => { 返回 void 的表达式 }
|
|
184
|
+
"@typescript-eslint/no-confusing-void-expression": "error"
|
|
174
185
|
}
|
|
175
186
|
}
|
package/Terminal.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'xterm/css/xterm.css';
|
|
2
|
+
import { Terminal as XTermTerminal } from 'xterm';
|
|
3
|
+
import { Model } from 'react-object-model';
|
|
4
|
+
import type { Remote } from './net.browser.js';
|
|
5
|
+
export declare function Terminal({ font }: {
|
|
6
|
+
font: string;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare class TerminalModel extends Model<TerminalModel> {
|
|
9
|
+
term: XTermTerminal;
|
|
10
|
+
stdio_id: number;
|
|
11
|
+
subscribe_stdio(remote: Remote): Promise<void>;
|
|
12
|
+
unsubscribe_stdio(remote: Remote): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare let terminal: TerminalModel;
|
|
15
|
+
export {};
|
package/Terminal.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import 'xterm/css/xterm.css';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { Terminal as XTermTerminal } from 'xterm';
|
|
5
|
+
import { FitAddon } from 'xterm-addon-fit';
|
|
6
|
+
import { WebglAddon } from 'xterm-addon-webgl';
|
|
7
|
+
import { Model } from 'react-object-model';
|
|
8
|
+
import { assert, genid } from './utils.browser.js';
|
|
9
|
+
export function Terminal({ font }) {
|
|
10
|
+
let rterminal = useRef();
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
(async () => {
|
|
13
|
+
await document.fonts.ready;
|
|
14
|
+
let term = new XTermTerminal({
|
|
15
|
+
fontFamily: font,
|
|
16
|
+
fontSize: 16,
|
|
17
|
+
cursorStyle: 'bar',
|
|
18
|
+
disableStdin: true,
|
|
19
|
+
convertEol: true,
|
|
20
|
+
allowProposedApi: true,
|
|
21
|
+
theme: {
|
|
22
|
+
...myscheme,
|
|
23
|
+
magenta: myscheme.purple,
|
|
24
|
+
brightMagenta: myscheme.brightPurple,
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const fit_addon = new FitAddon();
|
|
28
|
+
term.loadAddon(fit_addon);
|
|
29
|
+
term.open(rterminal.current);
|
|
30
|
+
term.loadAddon(new WebglAddon());
|
|
31
|
+
fit_addon.fit();
|
|
32
|
+
terminal.set({ term });
|
|
33
|
+
})();
|
|
34
|
+
}, []);
|
|
35
|
+
return _jsx("div", { className: 'term', ref: rterminal });
|
|
36
|
+
}
|
|
37
|
+
class TerminalModel extends Model {
|
|
38
|
+
term;
|
|
39
|
+
stdio_id = 0;
|
|
40
|
+
async subscribe_stdio(remote) {
|
|
41
|
+
assert(!this.stdio_id);
|
|
42
|
+
const id = this.stdio_id = genid();
|
|
43
|
+
remote.handlers.set(id, ({ data: [chunk] }) => {
|
|
44
|
+
this.term.write(chunk);
|
|
45
|
+
});
|
|
46
|
+
await remote.send({ id, func: 'subscribe_stdio' });
|
|
47
|
+
}
|
|
48
|
+
async unsubscribe_stdio(remote) {
|
|
49
|
+
let { stdio_id } = this;
|
|
50
|
+
assert(stdio_id);
|
|
51
|
+
await remote.call('unsubscribe_stdio', [stdio_id]);
|
|
52
|
+
remote.handlers.delete(stdio_id);
|
|
53
|
+
this.stdio_id = 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export let terminal = new TerminalModel();
|
|
57
|
+
/** winterm.json */
|
|
58
|
+
const myscheme = {
|
|
59
|
+
background: '#FFFFFF',
|
|
60
|
+
black: '#000000',
|
|
61
|
+
blue: '#0070C0',
|
|
62
|
+
brightBlack: '#444444',
|
|
63
|
+
brightBlue: '#0000FF',
|
|
64
|
+
brightCyan: '#008080',
|
|
65
|
+
brightGreen: '#718C00',
|
|
66
|
+
brightPurple: '#8959A8',
|
|
67
|
+
brightRed: '#DF0000',
|
|
68
|
+
brightWhite: '#AAAAAA',
|
|
69
|
+
brightYellow: '#FF81FF',
|
|
70
|
+
cursorColor: '#000000',
|
|
71
|
+
cyan: '#821717',
|
|
72
|
+
foreground: '#000000',
|
|
73
|
+
green: '#008000',
|
|
74
|
+
name: 'myscheme',
|
|
75
|
+
purple: '#800080',
|
|
76
|
+
red: '#C82829',
|
|
77
|
+
selectionBackground: '#ADD6FF',
|
|
78
|
+
white: '#888888',
|
|
79
|
+
yellow: '#EC7600'
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=Terminal.js.map
|
package/file.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { promises as fsp, default as fs } from 'fs';
|
|
|
5
5
|
type FileHandle = fsp.FileHandle & {
|
|
6
6
|
fp: string;
|
|
7
7
|
};
|
|
8
|
+
export { fsp };
|
|
8
9
|
export type Encoding = 'utf-8' | 'gb18030' | 'shift-jis' | 'utf-16le';
|
|
9
10
|
export declare const encodings: readonly ["utf-8", "gb18030", "shift-jis", "utf-16le"];
|
|
10
11
|
/** fp 所指向的 文件/ 文件夹 是否存在
|
|
@@ -85,8 +86,8 @@ export interface FListOptions {
|
|
|
85
86
|
- deep?: `false` 递归遍历 recursively
|
|
86
87
|
- absolute?: `false` 返回、打印完整路径而不是相对路径,为 false 时返回的 FStats.fp 也会被修改 Return, print full path instead of relative path, FStats.fp returned when false will also be modified
|
|
87
88
|
- print?: `true`
|
|
88
|
-
- filter?: `true` RegExp | (fp: string) => any
|
|
89
|
-
|
|
89
|
+
- filter?: `() => true` RegExp | (fp: string) => any
|
|
90
|
+
注意当 deep = true 时被 filter 过滤掉的目录及目录中的文件不会包含在结果中
|
|
90
91
|
- stats?: `false` 启用后返回 FStats 列表
|
|
91
92
|
Returns the FStats list when enabled */
|
|
92
93
|
export declare function flist(fpd: string): Promise<string[]>;
|
|
@@ -115,12 +116,14 @@ export declare function fdelete(fp: string, { print }?: {
|
|
|
115
116
|
- options?:
|
|
116
117
|
- print?: `true`
|
|
117
118
|
- overwrite?: `true`
|
|
118
|
-
|
|
119
|
+
- filter?: 当 fp_src 为文件夹时选择性复制里面的部分内容,和 flist 的 filter 选项一样,但只支持文件夹中第一层的文件
|
|
120
|
+
|
|
119
121
|
@example
|
|
120
122
|
fcopy('D:/temp/camera/', 'D:/camera/') */
|
|
121
|
-
export declare function fcopy(fp_src: string, fp_dst: string, { print, overwrite, }?: {
|
|
123
|
+
export declare function fcopy(fp_src: string, fp_dst: string, { print, overwrite, filter, }?: {
|
|
122
124
|
print?: boolean;
|
|
123
125
|
overwrite?: boolean;
|
|
126
|
+
filter?: FListOptions['filter'];
|
|
124
127
|
}): Promise<void>;
|
|
125
128
|
/** 移动文件或文件夹
|
|
126
129
|
相同分区 / 文件系统下使用 rename, 否则 fallback 到复制后删除源文件
|
|
@@ -177,4 +180,3 @@ export declare function ftail(fp: string, handler: (lines: string[]) => void | P
|
|
|
177
180
|
export declare function freplace(fp: string, pattern: string | RegExp, replacement: string, { print }?: {
|
|
178
181
|
print?: boolean;
|
|
179
182
|
}): Promise<void>;
|
|
180
|
-
export {};
|
package/file.js
CHANGED
|
@@ -4,6 +4,7 @@ import { path } from './path.js';
|
|
|
4
4
|
import { t } from './i18n/instance.js';
|
|
5
5
|
import { to_json } from './prototype.js';
|
|
6
6
|
import { assert, Lock } from './utils.js';
|
|
7
|
+
export { fsp };
|
|
7
8
|
export const encodings = ['utf-8', 'gb18030', 'shift-jis', 'utf-16le'];
|
|
8
9
|
/** fp 所指向的 文件/ 文件夹 是否存在
|
|
9
10
|
Does the file/folder pointed to by fp exist? */
|
|
@@ -225,22 +226,31 @@ export async function fdelete(fp, { print = true } = {}) {
|
|
|
225
226
|
- options?:
|
|
226
227
|
- print?: `true`
|
|
227
228
|
- overwrite?: `true`
|
|
228
|
-
|
|
229
|
+
- filter?: 当 fp_src 为文件夹时选择性复制里面的部分内容,和 flist 的 filter 选项一样,但只支持文件夹中第一层的文件
|
|
230
|
+
|
|
229
231
|
@example
|
|
230
232
|
fcopy('D:/temp/camera/', 'D:/camera/') */
|
|
231
|
-
export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, } = {}) {
|
|
233
|
+
export async function fcopy(fp_src, fp_dst, { print = true, overwrite = true, filter, } = {}) {
|
|
232
234
|
const { isdir } = fp_src;
|
|
233
235
|
assert(isdir === fp_dst.isdir, t('fp_src 和 fp_dst 必须同为文件路径或文件夹路径'));
|
|
234
236
|
assert(path.isAbsolute(fp_src) && path.isAbsolute(fp_dst), t('fp_src 和 fp_dst 必须为完整路径'));
|
|
237
|
+
if (!isdir && filter)
|
|
238
|
+
throw new Error(t('filter 选项只适用于 fp_src 为文件夹'));
|
|
235
239
|
if (print)
|
|
236
240
|
console.log(t('复制'), fp_src, '->', fp_dst);
|
|
237
241
|
if (isdir)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
if (filter) {
|
|
243
|
+
await fmkdir(fp_dst, { print });
|
|
244
|
+
await Promise.all((await flist(fp_src, { filter, print: false }))
|
|
245
|
+
.map(async (fname) => fcopy(fp_src + fname, fp_dst + fname, { print, overwrite })));
|
|
246
|
+
}
|
|
247
|
+
else
|
|
248
|
+
await fsp.cp(fp_src, fp_dst, {
|
|
249
|
+
recursive: true,
|
|
250
|
+
force: overwrite,
|
|
251
|
+
errorOnExist: !overwrite,
|
|
252
|
+
mode: overwrite ? 0 : fs.constants.COPYFILE_EXCL,
|
|
253
|
+
});
|
|
244
254
|
else
|
|
245
255
|
try {
|
|
246
256
|
await fsp.copyFile(fp_src, fp_dst, overwrite ? 0 : fs.constants.COPYFILE_EXCL);
|
|
@@ -269,14 +279,14 @@ export async function fmove(fp_src, fp_dst, { overwrite = false, print = true }
|
|
|
269
279
|
assert(path.isAbsolute(fp_src) && path.isAbsolute(fp_dst), t('fp_src 和 fp_dst 必须为完整路径'));
|
|
270
280
|
if (print)
|
|
271
281
|
console.log(t('移动'), fp_src, '->', fp_dst);
|
|
272
|
-
if (!overwrite && fexists(fp_dst))
|
|
282
|
+
if (!overwrite && fexists(fp_dst, { print: false }))
|
|
273
283
|
throw new Error(`${t('已存在')} ${fp_dst}`);
|
|
274
284
|
await fmkdir(fp_dst.fdir, { print: false });
|
|
275
285
|
async function copy_and_delete() {
|
|
276
|
-
await fcopy(fp_src, fp_dst, { overwrite, print });
|
|
277
|
-
await fdelete(fp_src, { print });
|
|
286
|
+
await fcopy(fp_src, fp_dst, { overwrite, print: false });
|
|
287
|
+
await fdelete(fp_src, { print: false });
|
|
278
288
|
}
|
|
279
|
-
if (fp_src[0] !== fp_dst[0])
|
|
289
|
+
if (fp_src[0] !== fp_dst[0] || overwrite)
|
|
280
290
|
await copy_and_delete();
|
|
281
291
|
else
|
|
282
292
|
try {
|
|
@@ -305,7 +315,7 @@ export async function frename(fp, fp_, { fpd, print = true, overwrite = true } =
|
|
|
305
315
|
assert(path.isAbsolute(fp) && path.isAbsolute(fp_), t('fp 和 fp_ 必须是绝对路径'));
|
|
306
316
|
if (print)
|
|
307
317
|
console.log(t('重命名'), fp, '->', fp_);
|
|
308
|
-
if (!overwrite && fexists(fp_))
|
|
318
|
+
if (!overwrite && fexists(fp_, { print: false }))
|
|
309
319
|
throw new Error(`${t('已存在')} ${fp}`);
|
|
310
320
|
await fsp.rename(fp, fp_);
|
|
311
321
|
}
|
|
@@ -349,9 +359,9 @@ export async function flink(fp_real, fp_link, { junction = false, print = true }
|
|
|
349
359
|
const is_fpd_link = fp_link.isdir;
|
|
350
360
|
assert(is_fpd_real === is_fpd_link, t('fp_real 和 fp_link 必须同为文件路径或文件夹路径'));
|
|
351
361
|
if (fexists(fp_link))
|
|
352
|
-
throw new Error(t('存在同名')
|
|
362
|
+
throw new Error(`${t('存在同名')}${is_fpd_link ? t('文件夹') : t('文件')}: ${fp_link}${t(',无法创建链接')}`);
|
|
353
363
|
if (print)
|
|
354
|
-
console.log(t('已将源文件 ')
|
|
364
|
+
console.log(`${t('已将源文件 ')}${fp_real}${t(' 链接到 ')}${fp_link}`);
|
|
355
365
|
if (junction)
|
|
356
366
|
fsp.symlink(fp_real, fp_link, 'junction');
|
|
357
367
|
else
|
package/i18n/dict.json
CHANGED
package/i18n/scanner/index.js
CHANGED
|
@@ -18,7 +18,7 @@ const DEFAULT_CONFIG = {
|
|
|
18
18
|
debug: false,
|
|
19
19
|
input: [
|
|
20
20
|
// 'src/**/*.{js,jsx,ts,tsx}',
|
|
21
|
-
'!i18n/**',
|
|
21
|
+
'!i18n/**', // Use ! to filter out files or directories
|
|
22
22
|
'!node_modules/**',
|
|
23
23
|
'!**/*.d.ts',
|
|
24
24
|
],
|
|
@@ -35,7 +35,7 @@ const DEFAULT_CONFIG = {
|
|
|
35
35
|
extensions: [], // 避免在 transform 中执行原生的 parseFuncFromString
|
|
36
36
|
},
|
|
37
37
|
trans: {
|
|
38
|
-
extensions: [],
|
|
38
|
+
extensions: [], // 避免在 transform 中执行原生的 parseTransFromString
|
|
39
39
|
fallbackKey: true,
|
|
40
40
|
babylon: {
|
|
41
41
|
sourceType: 'module',
|
|
@@ -74,22 +74,22 @@ const DEFAULT_CONFIG = {
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
// 禁用 : 和 . 作为 seperator
|
|
77
|
-
keySeparator: false,
|
|
78
|
-
nsSeparator: false,
|
|
77
|
+
keySeparator: false, // char to separate keys
|
|
78
|
+
nsSeparator: false, // char to split namespace from key
|
|
79
79
|
// Context Form
|
|
80
|
-
context: true,
|
|
81
|
-
contextFallback: true,
|
|
82
|
-
contextSeparator: '_',
|
|
80
|
+
context: true, // whether to add context form key
|
|
81
|
+
contextFallback: true, // whether to add a fallback key as well as the context form key
|
|
82
|
+
contextSeparator: '_', // char to split context from key
|
|
83
83
|
// Plural
|
|
84
84
|
// whether to add plural form key
|
|
85
85
|
plural(language, ns, key, options /** Config */) {
|
|
86
86
|
return language === 'en';
|
|
87
87
|
},
|
|
88
|
-
pluralFallback: true,
|
|
89
|
-
pluralSeparator: '_',
|
|
88
|
+
pluralFallback: true, // whether to add a fallback key as well as the plural form key
|
|
89
|
+
pluralSeparator: '_', // char to split plural from key
|
|
90
90
|
// interpolation options
|
|
91
91
|
interpolation: {
|
|
92
|
-
prefix: '{{',
|
|
92
|
+
prefix: '{{', // prefix for interpolation
|
|
93
93
|
suffix: '}}' // suffix for interpolation
|
|
94
94
|
}
|
|
95
95
|
};
|
package/net.js
CHANGED
|
@@ -87,7 +87,7 @@ 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/119.0.0.0 Safari/537.36',
|
|
91
91
|
'sec-ch-ua-platform': '"Windows"',
|
|
92
92
|
'sec-ch-ua-platform-version': '"15.0.0"',
|
|
93
93
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xshell",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.61",
|
|
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": ">=
|
|
19
|
+
"node": ">=21.3.0",
|
|
20
20
|
"vscode": ">=1.81.0"
|
|
21
21
|
},
|
|
22
22
|
"author": "ShenHongFei <shen.hongfei@outlook.com> (https://github.com/ShenHongFei)",
|
|
@@ -45,23 +45,23 @@
|
|
|
45
45
|
]
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@babel/core": "^7.
|
|
49
|
-
"@babel/parser": "^7.
|
|
50
|
-
"@babel/traverse": "^7.
|
|
48
|
+
"@babel/core": "^7.23.5",
|
|
49
|
+
"@babel/parser": "^7.23.5",
|
|
50
|
+
"@babel/traverse": "^7.23.5",
|
|
51
51
|
"@koa/cors": "^4.0.0",
|
|
52
|
-
"@types/ws": "^8.5.
|
|
52
|
+
"@types/ws": "^8.5.10",
|
|
53
53
|
"byte-size": "^8.1.1",
|
|
54
54
|
"chalk": "^5.3.0",
|
|
55
|
-
"chardet": "^
|
|
55
|
+
"chardet": "^2.0.0",
|
|
56
56
|
"cli-table3": "^0.6.3",
|
|
57
|
-
"cli-truncate": "^
|
|
57
|
+
"cli-truncate": "^4.0.0",
|
|
58
58
|
"colors": "^1.4.0",
|
|
59
|
-
"commander": "^11.
|
|
60
|
-
"emoji-regex": "^10.
|
|
59
|
+
"commander": "^11.1.0",
|
|
60
|
+
"emoji-regex": "^10.3.0",
|
|
61
61
|
"fetch-cookie": "^2.1.0",
|
|
62
62
|
"gulp-sort": "^2.0.0",
|
|
63
63
|
"hash-string": "^1.0.0",
|
|
64
|
-
"i18next": "^23.
|
|
64
|
+
"i18next": "^23.7.7",
|
|
65
65
|
"i18next-scanner": "^4.4.0",
|
|
66
66
|
"js-cookie": "^3.0.5",
|
|
67
67
|
"koa": "^2.14.2",
|
|
@@ -71,42 +71,47 @@
|
|
|
71
71
|
"mime-types": "^2.1.35",
|
|
72
72
|
"ora": "^7.0.1",
|
|
73
73
|
"react": "^18.2.0",
|
|
74
|
-
"react-i18next": "^13.
|
|
74
|
+
"react-i18next": "^13.5.0",
|
|
75
|
+
"react-object-model": "^1.2.0",
|
|
75
76
|
"resolve-path": "^1.4.0",
|
|
76
77
|
"strip-ansi": "^7.1.0",
|
|
77
78
|
"through2": "^4.0.2",
|
|
78
79
|
"tough-cookie": "^4.1.3",
|
|
79
80
|
"tslib": "^2.6.2",
|
|
80
|
-
"typescript": "^5.
|
|
81
|
+
"typescript": "^5.3.2",
|
|
81
82
|
"ua-parser-js": "2.0.0-alpha.2",
|
|
82
|
-
"undici": "^5.
|
|
83
|
+
"undici": "^5.28.2",
|
|
83
84
|
"vinyl": "^3.0.0",
|
|
84
85
|
"vinyl-fs": "^4.0.0",
|
|
85
|
-
"ws": "^8.14.
|
|
86
|
+
"ws": "^8.14.2",
|
|
87
|
+
"xterm": "^5.3.0",
|
|
88
|
+
"xterm-addon-fit": "^0.8.0",
|
|
89
|
+
"xterm-addon-web-links": "^0.9.0",
|
|
90
|
+
"xterm-addon-webgl": "^0.16.0"
|
|
86
91
|
},
|
|
87
92
|
"devDependencies": {
|
|
88
|
-
"@babel/types": "^7.
|
|
89
|
-
"@types/babel__traverse": "^7.20.
|
|
90
|
-
"@types/byte-size": "^8.1.
|
|
91
|
-
"@types/chardet": "^0.8.
|
|
92
|
-
"@types/gulp-sort": "2.0.
|
|
93
|
-
"@types/js-cookie": "^3.0.
|
|
94
|
-
"@types/koa": "^2.13.
|
|
95
|
-
"@types/koa-compress": "^4.0.
|
|
96
|
-
"@types/lodash": "^4.14.
|
|
97
|
-
"@types/mime-types": "^2.1.
|
|
98
|
-
"@types/node": "^20.
|
|
99
|
-
"@types/react": "^18.2.
|
|
100
|
-
"@types/through2": "^2.0.
|
|
101
|
-
"@types/tough-cookie": "^4.0.
|
|
102
|
-
"@types/ua-parser-js": "^0.7.
|
|
103
|
-
"@types/vinyl-fs": "^3.0.
|
|
104
|
-
"@types/vscode": "^1.
|
|
105
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
106
|
-
"@typescript-eslint/parser": "^6.
|
|
107
|
-
"eslint": "^8.
|
|
93
|
+
"@babel/types": "^7.23.5",
|
|
94
|
+
"@types/babel__traverse": "^7.20.4",
|
|
95
|
+
"@types/byte-size": "^8.1.2",
|
|
96
|
+
"@types/chardet": "^0.8.3",
|
|
97
|
+
"@types/gulp-sort": "2.0.4",
|
|
98
|
+
"@types/js-cookie": "^3.0.6",
|
|
99
|
+
"@types/koa": "^2.13.12",
|
|
100
|
+
"@types/koa-compress": "^4.0.6",
|
|
101
|
+
"@types/lodash": "^4.14.202",
|
|
102
|
+
"@types/mime-types": "^2.1.4",
|
|
103
|
+
"@types/node": "^20.10.3",
|
|
104
|
+
"@types/react": "^18.2.41",
|
|
105
|
+
"@types/through2": "^2.0.41",
|
|
106
|
+
"@types/tough-cookie": "^4.0.5",
|
|
107
|
+
"@types/ua-parser-js": "^0.7.39",
|
|
108
|
+
"@types/vinyl-fs": "^3.0.5",
|
|
109
|
+
"@types/vscode": "^1.84.2",
|
|
110
|
+
"@typescript-eslint/eslint-plugin": "^6.13.1",
|
|
111
|
+
"@typescript-eslint/parser": "^6.13.1",
|
|
112
|
+
"eslint": "^8.55.0",
|
|
108
113
|
"eslint-plugin-react": "^7.33.2",
|
|
109
|
-
"eslint-plugin-xlint": "^1.0.
|
|
114
|
+
"eslint-plugin-xlint": "^1.0.10"
|
|
110
115
|
},
|
|
111
116
|
"scripts": {
|
|
112
117
|
"start": "node --title=xshell --inspect=0.0.0.0:8420 ./xshell.js",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# 用来修复 `byte_size()` 函数的 ts 类型报错
|
|
2
2
|
|
|
3
3
|
diff --git a/package.json b/package.json
|
|
4
|
-
index
|
|
4
|
+
index dd8ed66fcb8b1ab93f0a3a015bad95bc31d3b18d..6a3753f42610451163fd7d502a93779fe3dca8db 100644
|
|
5
5
|
--- a/package.json
|
|
6
6
|
+++ b/package.json
|
|
7
7
|
@@ -11,6 +11,7 @@
|
|
8
|
-
"
|
|
8
|
+
"url": "https://github.com/lntel"
|
|
9
9
|
}
|
|
10
10
|
],
|
|
11
11
|
+ "type": "module",
|
|
12
12
|
"main": "",
|
|
13
13
|
"types": "index.d.ts",
|
|
14
|
-
"repository": {
|
|
14
|
+
"repository": {
|
package/process.d.ts
CHANGED
|
@@ -9,8 +9,8 @@ export declare const exe_nodejs: string;
|
|
|
9
9
|
interface StartOptions {
|
|
10
10
|
/** `继承当前工作目录 process.cwd()` 子进程的工作目录 `inherit the cwd` cwd of the child process. */
|
|
11
11
|
cwd?: string;
|
|
12
|
-
/** `process.env` 覆盖/添加到 process.env 的环境变量
|
|
13
|
-
|
|
12
|
+
/** `process.env` 覆盖/添加到 process.env 的环境变量 */
|
|
13
|
+
envs?: Record<string, string>;
|
|
14
14
|
/** `'utf-8'` 子进程输出编码 child output encoding */
|
|
15
15
|
encoding?: Encoding | 'binary';
|
|
16
16
|
/** `true` print 选项,支持设置细项 print option (with details) */
|
|
@@ -18,6 +18,7 @@ interface StartOptions {
|
|
|
18
18
|
stdout: boolean;
|
|
19
19
|
stderr: boolean;
|
|
20
20
|
command: boolean;
|
|
21
|
+
/** 进程执行成功的消息 */
|
|
21
22
|
code: boolean;
|
|
22
23
|
};
|
|
23
24
|
/** `'pipe'` 设置为 'ignore' 时忽略 stdio 处理 when 'ignore' then ignore stdio processing */
|
|
@@ -41,13 +42,13 @@ interface StartOptions {
|
|
|
41
42
|
- args: `[]` 参数列表 arguments list
|
|
42
43
|
- options
|
|
43
44
|
- cwd?: `继承当前工作目录 process.cwd()` 子进程的工作目录 `inherit the cwd` cwd of the child process.
|
|
44
|
-
-
|
|
45
|
+
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量
|
|
45
46
|
- encoding?: `'utf-8'` 子进程输出编码 child output encoding
|
|
46
47
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
47
48
|
- stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理 when 'ignore' then ignore stdio processing
|
|
48
49
|
- detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref) whether to break the connection with child (ignore stdio, unref)
|
|
49
50
|
*/
|
|
50
|
-
export declare function start(exe: string, args?: string[], { cwd, encoding, print, stdio, detached,
|
|
51
|
+
export declare function start(exe: string, args?: string[], { cwd, encoding, print, stdio, detached, envs, window: _window, }?: StartOptions): Promise<ChildProcess>;
|
|
51
52
|
export declare function start_nodejs(js: string, args?: string[], options?: StartOptions): Promise<ChildProcess>;
|
|
52
53
|
export interface CallOptions extends StartOptions {
|
|
53
54
|
throw_code?: boolean;
|
|
@@ -67,7 +68,7 @@ export interface CallResult<TOutput extends string | Buffer = string> {
|
|
|
67
68
|
- args: `[]` 参数列表 arguments list
|
|
68
69
|
- options?:
|
|
69
70
|
- cwd?: `process.cwd()`
|
|
70
|
-
-
|
|
71
|
+
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量 overwrite/add to process.env
|
|
71
72
|
- encoding?: `'utf-8'` 子进程输出编码, 设置为 binary 时返回 stdout, stderr 类型为 Buffer; 否则是 string
|
|
72
73
|
child output encoding. When set to binary, return stdout, stderr is of type Buffer; otherwise it is string
|
|
73
74
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
@@ -80,18 +81,22 @@ export declare function call(exe: string, args?: string[], options?: CallOptions
|
|
|
80
81
|
encoding: 'binary';
|
|
81
82
|
}): Promise<CallResult<Buffer>>;
|
|
82
83
|
export declare function call(exe: string, args?: string[], options?: CallOptions): Promise<CallResult<string>>;
|
|
84
|
+
export interface CallNodeJsOptions extends CallOptions {
|
|
85
|
+
inspect?: number;
|
|
86
|
+
break?: boolean;
|
|
87
|
+
}
|
|
83
88
|
/** call node <js> for result
|
|
84
89
|
- js: .js 路径 (相对路径根据 cwd 解析) path (relative path will resolve based on cwd)
|
|
85
90
|
- args: `[]` 参数列表 arguments list
|
|
86
91
|
- options
|
|
87
92
|
- cwd?: `process.cwd()`
|
|
88
|
-
-
|
|
93
|
+
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量 overwrite/add to process.env
|
|
89
94
|
- encoding?: `'utf-8'` 子进程输出编码 child process output encoding
|
|
90
95
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
91
96
|
- stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理 when 'ignore' then ignore stdio processing
|
|
92
97
|
- detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref) whether to break the connection with child (ignore stdio, unref)
|
|
93
98
|
- throw_code?: `true` code 不为 0 时是否抛出异常 whether to throw Error when code is not 0 */
|
|
94
|
-
export declare function call_nodejs(js: string, args?: string[], options?:
|
|
99
|
+
export declare function call_nodejs(js: string, args?: string[], { inspect, break: _break, ...options }?: CallNodeJsOptions): Promise<CallResult<string>>;
|
|
95
100
|
export interface TermOptions {
|
|
96
101
|
/** `process.cwd()` */
|
|
97
102
|
cwd?: string;
|
package/process.js
CHANGED
|
@@ -9,20 +9,20 @@ export const exe_nodejs = process.execPath.fp;
|
|
|
9
9
|
- args: `[]` 参数列表 arguments list
|
|
10
10
|
- options
|
|
11
11
|
- cwd?: `继承当前工作目录 process.cwd()` 子进程的工作目录 `inherit the cwd` cwd of the child process.
|
|
12
|
-
-
|
|
12
|
+
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量
|
|
13
13
|
- encoding?: `'utf-8'` 子进程输出编码 child output encoding
|
|
14
14
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
15
15
|
- stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理 when 'ignore' then ignore stdio processing
|
|
16
16
|
- detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref) whether to break the connection with child (ignore stdio, unref)
|
|
17
17
|
*/
|
|
18
|
-
export async function start(exe, args = [], { cwd, encoding = 'utf-8', print = true, stdio = 'pipe', detached = false,
|
|
18
|
+
export async function start(exe, args = [], { cwd, encoding = 'utf-8', print = true, stdio = 'pipe', detached = false, envs, window: _window = true, } = {}) {
|
|
19
19
|
const options = {
|
|
20
20
|
cwd,
|
|
21
21
|
shell: false,
|
|
22
22
|
windowsHide: !_window,
|
|
23
23
|
stdio,
|
|
24
|
-
...
|
|
25
|
-
env: { ...process.env, ...
|
|
24
|
+
...envs ? {
|
|
25
|
+
env: { ...process.env, ...envs }
|
|
26
26
|
} : {},
|
|
27
27
|
};
|
|
28
28
|
if (typeof print === 'boolean')
|
|
@@ -169,14 +169,20 @@ export async function call(exe, args = [], options = {}) {
|
|
|
169
169
|
- args: `[]` 参数列表 arguments list
|
|
170
170
|
- options
|
|
171
171
|
- cwd?: `process.cwd()`
|
|
172
|
-
-
|
|
172
|
+
- envs?: `process.env` 覆盖/添加到 process.env 的环境变量 overwrite/add to process.env
|
|
173
173
|
- encoding?: `'utf-8'` 子进程输出编码 child process output encoding
|
|
174
174
|
- print?: `true` print 选项,支持设置细项 print option (with details)
|
|
175
175
|
- stdio?: `'pipe'` 设置为 'ignore' 时忽略 stdio 处理 when 'ignore' then ignore stdio processing
|
|
176
176
|
- detached?: `false` 是否断开和 child 的关系 (ignore stdio, unref) whether to break the connection with child (ignore stdio, unref)
|
|
177
177
|
- throw_code?: `true` code 不为 0 时是否抛出异常 whether to throw Error when code is not 0 */
|
|
178
|
-
export async function call_nodejs(js, args = [], options) {
|
|
179
|
-
return call(exe_nodejs, [
|
|
178
|
+
export async function call_nodejs(js, args = [], { inspect, break: _break, ...options } = {}) {
|
|
179
|
+
return call(exe_nodejs, [
|
|
180
|
+
...inspect ? [
|
|
181
|
+
_break ? `--inspect-brk=localhost:${inspect}` : `--inspect=localhost:${inspect}`
|
|
182
|
+
] : [],
|
|
183
|
+
js,
|
|
184
|
+
...args
|
|
185
|
+
], options);
|
|
180
186
|
}
|
|
181
187
|
export const exe_winterm = `C:/Users/${userInfo().username}/AppData/Local/Microsoft/WindowsApps/wt.exe`;
|
|
182
188
|
/** 新建 terminal 窗口执行进程 New Terminal window execution process
|
package/prototype.browser.js
CHANGED
package/server.d.ts
CHANGED
|
@@ -63,11 +63,25 @@ export declare class Server {
|
|
|
63
63
|
_router(ctx: Context, next: Next): Promise<void>;
|
|
64
64
|
/** 被子类重写以自定义处理逻辑 */
|
|
65
65
|
router(ctx: Context): Promise<boolean>;
|
|
66
|
+
/** 重定向 ctx 到 url (可以是完整 url 也可以是路径),设置 status code 为 code */
|
|
67
|
+
redirect(ctx: Context, url: string, code?: 301 | 302): void;
|
|
66
68
|
logger(ctx: Context): void;
|
|
67
69
|
process_ua(ctx: Context): string;
|
|
68
70
|
format_ua(headers: IncomingHttpHeaders | IncomingHttp2Headers): string;
|
|
69
71
|
proxy(ctx: Context, url: string | URL, headers_?: Record<string, string>, redirect?: RequestOptions['redirect']): Promise<void>;
|
|
70
72
|
static filter_response_headers(headers: Headers): {};
|
|
73
|
+
/** 提供静态文件
|
|
74
|
+
@example
|
|
75
|
+
const prefix_docs = '/zh/'
|
|
76
|
+
if (path.startsWith(prefix_docs)) {
|
|
77
|
+
await this.try_send(
|
|
78
|
+
ctx,
|
|
79
|
+
`T:/t/docs${prefix_docs}`,
|
|
80
|
+
(path.endsWith('/') ? `${path}index.html` : path).slice(prefix_docs.length),
|
|
81
|
+
true
|
|
82
|
+
)
|
|
83
|
+
return
|
|
84
|
+
} */
|
|
71
85
|
try_send(ctx: Context, fpd_root: string, fp: string, log_404: boolean): Promise<boolean>;
|
|
72
86
|
/** 将 body 设置为 fp 所指文件的 ReadStream
|
|
73
87
|
检查 fp 是否合法,设置对应的 content-type,支持缓存,支持分块下载
|
package/server.js
CHANGED
|
@@ -70,7 +70,8 @@ export class Server {
|
|
|
70
70
|
this.server_ws = new WebSocketServer({
|
|
71
71
|
noServer: true,
|
|
72
72
|
skipUTF8Validation: true,
|
|
73
|
-
perMessageDeflate: true
|
|
73
|
+
perMessageDeflate: true,
|
|
74
|
+
maxPayload: 2 ** 30 // 1 GB
|
|
74
75
|
});
|
|
75
76
|
this.server_ws.on('connection', (ws, request) => {
|
|
76
77
|
ws.addEventListener('message', ({ data }) => {
|
|
@@ -170,10 +171,18 @@ export class Server {
|
|
|
170
171
|
async router(ctx) {
|
|
171
172
|
return false;
|
|
172
173
|
}
|
|
174
|
+
/** 重定向 ctx 到 url (可以是完整 url 也可以是路径),设置 status code 为 code */
|
|
175
|
+
redirect(ctx, url, code = 301) {
|
|
176
|
+
const { request: { _path } } = ctx;
|
|
177
|
+
let { response } = ctx;
|
|
178
|
+
response.redirect(url);
|
|
179
|
+
response.status = code;
|
|
180
|
+
console.log(`${code} 重定向 ${_path} -> ${url}`.yellow);
|
|
181
|
+
}
|
|
173
182
|
logger(ctx) {
|
|
174
183
|
const { colors } = this;
|
|
175
184
|
const { request } = ctx;
|
|
176
|
-
const { query, body, path, _path, protocol, host, req: { httpVersion: http_version }, ip, } = request;
|
|
185
|
+
const { query, body, path, _path, protocol, host, req: { httpVersion: http_version }, ip, headers, } = request;
|
|
177
186
|
let { method } = request;
|
|
178
187
|
let s = '';
|
|
179
188
|
// 时间
|
|
@@ -200,6 +209,16 @@ export class Server {
|
|
|
200
209
|
return colors ? path.yellow : path;
|
|
201
210
|
return path;
|
|
202
211
|
})();
|
|
212
|
+
// range
|
|
213
|
+
let range = headers.range;
|
|
214
|
+
if (headers.range) {
|
|
215
|
+
let [, start, end] = /(\d*)-(\d*)/.exec(range) || [];
|
|
216
|
+
if (start)
|
|
217
|
+
start = Number(start).to_fsize_str();
|
|
218
|
+
if (end)
|
|
219
|
+
end = Number(end).to_fsize_str();
|
|
220
|
+
s += ` (${start} - ${end || ''})`;
|
|
221
|
+
}
|
|
203
222
|
// query
|
|
204
223
|
if (Object.keys(query).length) {
|
|
205
224
|
let t = inspect(query, { compact: true })
|
|
@@ -310,6 +329,18 @@ export class Server {
|
|
|
310
329
|
headers_[key] = value;
|
|
311
330
|
return headers_;
|
|
312
331
|
}
|
|
332
|
+
/** 提供静态文件
|
|
333
|
+
@example
|
|
334
|
+
const prefix_docs = '/zh/'
|
|
335
|
+
if (path.startsWith(prefix_docs)) {
|
|
336
|
+
await this.try_send(
|
|
337
|
+
ctx,
|
|
338
|
+
`T:/t/docs${prefix_docs}`,
|
|
339
|
+
(path.endsWith('/') ? `${path}index.html` : path).slice(prefix_docs.length),
|
|
340
|
+
true
|
|
341
|
+
)
|
|
342
|
+
return
|
|
343
|
+
} */
|
|
313
344
|
async try_send(ctx, fpd_root, fp, log_404) {
|
|
314
345
|
const { request: { _path, path, method }, response, } = ctx;
|
|
315
346
|
if (response.body !== undefined || response.status !== 404)
|
|
@@ -342,7 +373,7 @@ export class Server {
|
|
|
342
373
|
- download?: `undefined` 在 response.headers 中加上 content-disposition: attachment 指示浏览器下载文件 */
|
|
343
374
|
async fsend(ctx, fp, { root, absolute, download, } = {}) {
|
|
344
375
|
assert(absolute || root?.isdir, t('fsend 必须传 absolute 选项或 root 文件夹'));
|
|
345
|
-
const { request } = ctx;
|
|
376
|
+
const { request, request: { method } } = ctx;
|
|
346
377
|
let { response } = ctx;
|
|
347
378
|
if (!absolute) {
|
|
348
379
|
if (fp.startsWith(root))
|
|
@@ -372,34 +403,44 @@ export class Server {
|
|
|
372
403
|
error.message = `fs.stat 出错: ${error.message}`;
|
|
373
404
|
throw error;
|
|
374
405
|
}
|
|
406
|
+
// 上面的 fstat 异步操作之后有可能 socket 已经关闭,那么这里什么都不做,直接结束吧
|
|
407
|
+
// 如果不这么做,后续 createReadStream 打开的流会赋给 response.body,但是 response.res 不再触发 finish, close 事件,
|
|
408
|
+
// 打开的流永远不会关闭,最终导致资源泄露,文件句柄一直打开
|
|
409
|
+
if (!response.socket) {
|
|
410
|
+
response.res.end();
|
|
411
|
+
return fp;
|
|
412
|
+
}
|
|
375
413
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Ranges
|
|
376
414
|
// advertise server support of partial requests
|
|
377
415
|
response.set('accept-ranges', 'bytes');
|
|
416
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#up-to-date_contents_always
|
|
378
417
|
if (!response.get('cache-control'))
|
|
379
|
-
response.set('cache-control', '
|
|
418
|
+
response.set('cache-control', 'no-cache');
|
|
419
|
+
const last_modified_str = stats.mtime.toUTCString();
|
|
380
420
|
if (!response.get('last-modified'))
|
|
381
|
-
response.set('last-modified',
|
|
421
|
+
response.set('last-modified', last_modified_str);
|
|
382
422
|
if (download)
|
|
383
423
|
response.set('content-disposition', `attachment; filename="${encodeURIComponent(fp.fname)}"`);
|
|
384
424
|
if (!response.get('content-type')) {
|
|
385
425
|
const { contentType: get_content_type } = await import('mime-types');
|
|
386
|
-
const fext = fp
|
|
387
|
-
response.set('content-type', (this.js_exts.has(fext) ? 'text/javascript; chatset=utf-8' : get_content_type(
|
|
426
|
+
const { fext } = fp;
|
|
427
|
+
response.set('content-type', (this.js_exts.has(fext) ? 'text/javascript; chatset=utf-8' : get_content_type(fext))
|
|
388
428
|
|| 'application/octet-stream');
|
|
389
429
|
}
|
|
390
|
-
|
|
430
|
+
const modified_since_str = request.get('if-modified-since');
|
|
431
|
+
if ((method === 'GET' || method === 'HEAD') && modified_since_str === last_modified_str) {
|
|
391
432
|
response.status = 304;
|
|
392
433
|
// 以上会自动设置 response.body = null
|
|
393
434
|
return fp;
|
|
394
435
|
}
|
|
395
|
-
|
|
436
|
+
const range_header = request.headers.range;
|
|
437
|
+
if (range_header)
|
|
396
438
|
try {
|
|
397
|
-
const range_header = request.headers.range;
|
|
398
439
|
const range_value = /=(.*)$/.exec(range_header)[1];
|
|
399
440
|
const range = /^[\w]*?(\d*)-(\d*)$/.exec(range_value);
|
|
400
441
|
assert(stats.size <= Number.MAX_SAFE_INTEGER);
|
|
401
|
-
let start = range[1] ?
|
|
402
|
-
let end = range[2] ?
|
|
442
|
+
let start = range[1] ? Number(range[1]) : undefined;
|
|
443
|
+
let end = range[2] ? Number(range[2]) : Number(stats.size) - 1;
|
|
403
444
|
if (start === undefined) {
|
|
404
445
|
start = Number(stats.size) - end;
|
|
405
446
|
end = Number(stats.size) - 1;
|
package/utils.browser.d.ts
CHANGED
|
@@ -52,7 +52,10 @@ export declare class Lock<TResource = void> {
|
|
|
52
52
|
export declare function pause(milliseconds?: number): Promise<void>;
|
|
53
53
|
/** 字符串字典序比较 */
|
|
54
54
|
export declare function strcmp(l: string, r: string): 0 | 1 | -1;
|
|
55
|
+
/** 比较 1.10.02 这种版本号 */
|
|
56
|
+
export declare function vercmp(l: string, r: string): number;
|
|
55
57
|
export declare function get<TReturn = any>(obj: any, keypath: string): TReturn;
|
|
58
|
+
export declare function global_get<TReturn = any>(keypath: string): TReturn;
|
|
56
59
|
export declare function invoke<TReturn = any>(obj: any, funcpath: string, args: any[]): TReturn;
|
|
57
60
|
/** 拼接 TypedArrays 生成一个完整的 Uint8Array */
|
|
58
61
|
export declare function concat(arrays: ArrayBufferView[]): Uint8Array;
|
|
@@ -71,3 +74,4 @@ export declare class Timer {
|
|
|
71
74
|
getstr(): string;
|
|
72
75
|
print(): void;
|
|
73
76
|
}
|
|
77
|
+
export declare function lowercase_first_letter(str: string): string;
|
package/utils.browser.js
CHANGED
|
@@ -130,12 +130,29 @@ export function strcmp(l, r) {
|
|
|
130
130
|
return -1;
|
|
131
131
|
return 1;
|
|
132
132
|
}
|
|
133
|
+
/** 比较 1.10.02 这种版本号 */
|
|
134
|
+
export function vercmp(l, r) {
|
|
135
|
+
const lparts = l.split('.').map(x => Number(x));
|
|
136
|
+
const rparts = r.split('.').map(x => Number(x));
|
|
137
|
+
assert(lparts.length === rparts.length, '传入 vercmp 的两个版本号应该格式一致');
|
|
138
|
+
for (let i = 0; i < lparts.length; i++) {
|
|
139
|
+
const l = lparts[i];
|
|
140
|
+
const r = rparts[i];
|
|
141
|
+
assert(!isNaN(l) && !isNaN(r), '传入 vercmp 的版本非法');
|
|
142
|
+
if (l !== r)
|
|
143
|
+
return l - r;
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
133
147
|
export function get(obj, keypath) {
|
|
134
148
|
let obj_ = obj;
|
|
135
149
|
for (const key of keypath.split('.'))
|
|
136
150
|
obj_ = obj_[key];
|
|
137
151
|
return obj_;
|
|
138
152
|
}
|
|
153
|
+
export function global_get(keypath) {
|
|
154
|
+
return get(globalThis, keypath);
|
|
155
|
+
}
|
|
139
156
|
export function invoke(obj, funcpath, args) {
|
|
140
157
|
const paths = funcpath.split('.');
|
|
141
158
|
let obj_ = obj;
|
|
@@ -197,4 +214,7 @@ export class Timer {
|
|
|
197
214
|
console.log(this.getstr());
|
|
198
215
|
}
|
|
199
216
|
}
|
|
217
|
+
export function lowercase_first_letter(str) {
|
|
218
|
+
return str[0].toLowerCase() + str.slice(1);
|
|
219
|
+
}
|
|
200
220
|
//# sourceMappingURL=utils.browser.js.map
|
package/utils.d.ts
CHANGED
|
@@ -23,7 +23,10 @@ export declare function unique<T>(iterable: T[] | Iterable<T>, selector?: string
|
|
|
23
23
|
export declare function sort_keys<T>(obj: T): T;
|
|
24
24
|
/** 字符串字典序比较 */
|
|
25
25
|
export declare function strcmp(l: string, r: string): 0 | 1 | -1;
|
|
26
|
+
/** 比较 1.10.02 这种版本号 */
|
|
27
|
+
export declare function vercmp(l: string, r: string): number;
|
|
26
28
|
export declare function get<TReturn = any>(obj: any, keypath: string): TReturn;
|
|
29
|
+
export declare function global_get<TReturn = any>(keypath: string): TReturn;
|
|
27
30
|
export declare function invoke<TReturn = any>(obj: any, funcpath: string, args: any[]): TReturn;
|
|
28
31
|
/** 拼接 TypedArrays 生成一个完整的 Uint8Array */
|
|
29
32
|
export declare function concat(arrays: ArrayBufferView[]): Uint8Array;
|
|
@@ -93,6 +96,7 @@ export declare class Lock<TResource = void> {
|
|
|
93
96
|
}
|
|
94
97
|
export declare function has_chinese(str: string): boolean;
|
|
95
98
|
export declare function escape_line_feed(str: string): string;
|
|
99
|
+
export declare function lowercase_first_letter(str: string): string;
|
|
96
100
|
/** util.inspect(obj)
|
|
97
101
|
- options
|
|
98
102
|
- limit?: `10000` 传 0 时不限制
|
package/utils.js
CHANGED
|
@@ -90,12 +90,29 @@ export function strcmp(l, r) {
|
|
|
90
90
|
return -1;
|
|
91
91
|
return 1;
|
|
92
92
|
}
|
|
93
|
+
/** 比较 1.10.02 这种版本号 */
|
|
94
|
+
export function vercmp(l, r) {
|
|
95
|
+
const lparts = l.split('.').map(x => Number(x));
|
|
96
|
+
const rparts = r.split('.').map(x => Number(x));
|
|
97
|
+
assert(lparts.length === rparts.length, '传入 vercmp 的两个版本号应该格式一致');
|
|
98
|
+
for (let i = 0; i < lparts.length; i++) {
|
|
99
|
+
const l = lparts[i];
|
|
100
|
+
const r = rparts[i];
|
|
101
|
+
assert(!isNaN(l) && !isNaN(r), '传入 vercmp 的版本非法');
|
|
102
|
+
if (l !== r)
|
|
103
|
+
return l - r;
|
|
104
|
+
}
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
93
107
|
export function get(obj, keypath) {
|
|
94
108
|
let obj_ = obj;
|
|
95
109
|
for (const key of keypath.split('.'))
|
|
96
110
|
obj_ = obj_[key];
|
|
97
111
|
return obj_;
|
|
98
112
|
}
|
|
113
|
+
export function global_get(keypath) {
|
|
114
|
+
return get(globalThis, keypath);
|
|
115
|
+
}
|
|
99
116
|
export function invoke(obj, funcpath, args) {
|
|
100
117
|
const paths = funcpath.split('.');
|
|
101
118
|
let obj_ = obj;
|
|
@@ -275,6 +292,9 @@ export function has_chinese(str) {
|
|
|
275
292
|
export function escape_line_feed(str) {
|
|
276
293
|
return str.replace(/\n/g, '\\n');
|
|
277
294
|
}
|
|
295
|
+
export function lowercase_first_letter(str) {
|
|
296
|
+
return str[0].toLowerCase() + str.slice(1);
|
|
297
|
+
}
|
|
278
298
|
/** util.inspect(obj)
|
|
279
299
|
- options
|
|
280
300
|
- limit?: `10000` 传 0 时不限制
|