@maiyunnet/kebab 2.0.2 → 2.0.4
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/index.js +1 -1
- package/lib/sql.js +1 -3
- package/lib/text.js +5 -1
- package/package.json +1 -1
- package/tsconfig.json +1 -1
- package/index.ts +0 -33
- package/lib/buffer.ts +0 -152
- package/lib/captcha.ts +0 -63
- package/lib/consistent.ts +0 -219
- package/lib/core.ts +0 -880
- package/lib/crypto.ts +0 -384
- package/lib/db.ts +0 -719
- package/lib/dns.ts +0 -405
- package/lib/fs.ts +0 -527
- package/lib/jwt.ts +0 -276
- package/lib/kv.ts +0 -1489
- package/lib/lan.ts +0 -87
- package/lib/net/formdata.ts +0 -166
- package/lib/net/request.ts +0 -150
- package/lib/net/response.ts +0 -59
- package/lib/net.ts +0 -662
- package/lib/s3.ts +0 -235
- package/lib/scan.ts +0 -364
- package/lib/session.ts +0 -230
- package/lib/sql.ts +0 -1151
- package/lib/ssh/sftp.ts +0 -508
- package/lib/ssh/shell.ts +0 -123
- package/lib/ssh.ts +0 -191
- package/lib/text.ts +0 -615
- package/lib/time.ts +0 -254
- package/lib/ws.ts +0 -523
- package/lib/zip.ts +0 -447
- package/lib/zlib.ts +0 -350
- package/main.ts +0 -27
- package/sys/child.ts +0 -678
- package/sys/cmd.ts +0 -225
- package/sys/ctr.ts +0 -904
- package/sys/master.ts +0 -355
- package/sys/mod.ts +0 -1871
- package/sys/route.ts +0 -1113
- package/types/index.d.ts +0 -283
- package/www/example/ctr/main.ts +0 -9
- package/www/example/ctr/middle.ts +0 -26
- package/www/example/ctr/test.ts +0 -3218
- package/www/example/mod/test.ts +0 -47
- package/www/example/mod/testdata.ts +0 -30
- package/www/example/ws/mproxy.ts +0 -16
- package/www/example/ws/rproxy.ts +0 -14
- package/www/example/ws/test.ts +0 -36
package/lib/text.ts
DELETED
|
@@ -1,615 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Project: Kebab, User: JianSuoQiYue
|
|
3
|
-
* Date: 2019-5-15 16:49:39
|
|
4
|
-
* Last: 2020-04-06 20:51:06, 2022-9-29 15:18:16, 2022-12-29 00:01:30, 2024-3-6 17:53:14, 2024-5-31 17:29:52, 2025-6-13 15:47:02
|
|
5
|
-
*/
|
|
6
|
-
import * as kebab from '~/index';
|
|
7
|
-
import * as fs from './fs';
|
|
8
|
-
import * as types from '~/types';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* --- 将文件大小格式化为带单位的字符串 ---
|
|
12
|
-
* @param size 文件大小
|
|
13
|
-
* @param spliter 分隔符
|
|
14
|
-
*/
|
|
15
|
-
export function sizeFormat(size: number, spliter: string = ' '): string {
|
|
16
|
-
const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
17
|
-
let i = 0;
|
|
18
|
-
for (; i < 6 && size >= 1024.0; ++i) {
|
|
19
|
-
size /= 1024.0;
|
|
20
|
-
}
|
|
21
|
-
return (Math.round(size * 100) / 100).toString() + spliter + units[i];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* --- 格式化一段 URL ---
|
|
26
|
-
* @param url
|
|
27
|
-
*/
|
|
28
|
-
export function parseUrl(url: string): types.IUrlParse {
|
|
29
|
-
// --- test: https://ab-3dc:aak9()$@github.com:80/nodejs/node/blob/master/lib/url.js?mail=abc@def.com#223 ---
|
|
30
|
-
const rtn: types.IUrlParse = {
|
|
31
|
-
'protocol': null,
|
|
32
|
-
'auth': null,
|
|
33
|
-
'user': null,
|
|
34
|
-
'pass': null,
|
|
35
|
-
'host': null,
|
|
36
|
-
'hostname': null,
|
|
37
|
-
'port': null,
|
|
38
|
-
'pathname': '/',
|
|
39
|
-
'path': null,
|
|
40
|
-
'query': null,
|
|
41
|
-
'hash': null
|
|
42
|
-
};
|
|
43
|
-
const hash = url.indexOf('#');
|
|
44
|
-
if (hash > -1) {
|
|
45
|
-
rtn['hash'] = url.slice(hash + 1);
|
|
46
|
-
url = url.slice(0, hash);
|
|
47
|
-
}
|
|
48
|
-
const query = url.indexOf('?');
|
|
49
|
-
if (query > -1) {
|
|
50
|
-
rtn['query'] = url.slice(query + 1);
|
|
51
|
-
url = url.slice(0, query);
|
|
52
|
-
}
|
|
53
|
-
const protocol = url.indexOf(':');
|
|
54
|
-
if (protocol > -1) {
|
|
55
|
-
rtn['protocol'] = url.slice(0, protocol + 1).toLowerCase();
|
|
56
|
-
url = url.slice(protocol + 1);
|
|
57
|
-
if (url.startsWith('//')) {
|
|
58
|
-
url = url.slice(2);
|
|
59
|
-
}
|
|
60
|
-
let path = url.indexOf('/');
|
|
61
|
-
if (path === -1) {
|
|
62
|
-
path = url.indexOf('\\');
|
|
63
|
-
}
|
|
64
|
-
if (path > -1) {
|
|
65
|
-
rtn['pathname'] = url.slice(path);
|
|
66
|
-
url = url.slice(0, path);
|
|
67
|
-
}
|
|
68
|
-
const auth = url.indexOf('@');
|
|
69
|
-
if (auth > -1) {
|
|
70
|
-
const authStr = url.slice(0, auth);
|
|
71
|
-
const authSplit = authStr.indexOf(':');
|
|
72
|
-
if (authSplit > -1) {
|
|
73
|
-
// --- 有密码 ---
|
|
74
|
-
rtn['user'] = authStr.slice(0, authSplit);
|
|
75
|
-
rtn['pass'] = authStr.slice(authSplit + 1);
|
|
76
|
-
rtn['auth'] = rtn['user'] + ':' + rtn['pass'];
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
rtn['user'] = authStr;
|
|
80
|
-
rtn['auth'] = authStr;
|
|
81
|
-
}
|
|
82
|
-
url = url.slice(auth + 1);
|
|
83
|
-
}
|
|
84
|
-
if (url) {
|
|
85
|
-
const port = url.indexOf(':');
|
|
86
|
-
if (port > -1) {
|
|
87
|
-
rtn['hostname'] = url.slice(0, port).toLowerCase();
|
|
88
|
-
rtn['port'] = url.slice(port + 1);
|
|
89
|
-
rtn['host'] = rtn['hostname'] + (rtn['port'] ? ':' + rtn['port'] : '');
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
rtn['hostname'] = url.toLowerCase();
|
|
93
|
-
rtn['host'] = rtn['hostname'];
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
// --- 没有 protocol ---
|
|
99
|
-
rtn['pathname'] = url;
|
|
100
|
-
}
|
|
101
|
-
// --- 组合 ---
|
|
102
|
-
rtn['path'] = rtn['pathname'] + (rtn['query'] ? '?' + rtn['query'] : '');
|
|
103
|
-
return rtn;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* --- 将相对路径根据基准路径进行转换 ---
|
|
108
|
-
* @param from 基准路径
|
|
109
|
-
* @param to 相对路径
|
|
110
|
-
*/
|
|
111
|
-
export function urlResolve(from: string, to: string): string {
|
|
112
|
-
from = from.replace('\\', '/');
|
|
113
|
-
to = to.replace('\\', '/');
|
|
114
|
-
// --- to 为空,直接返回 form ---
|
|
115
|
-
if (to === '') {
|
|
116
|
-
return urlAtom(from);
|
|
117
|
-
}
|
|
118
|
-
// --- 获取 from 的 scheme, host, path ---
|
|
119
|
-
const f = parseUrl(from);
|
|
120
|
-
// --- 以 // 开头的,加上 from 的 protocol 返回 ---
|
|
121
|
-
if (to.startsWith('//')) {
|
|
122
|
-
return urlAtom(f.protocol ? f.protocol + to : to);
|
|
123
|
-
}
|
|
124
|
-
if (f.protocol) {
|
|
125
|
-
// --- 获取小写的 protocol ---
|
|
126
|
-
from = f.protocol + from.slice(f.protocol.length);
|
|
127
|
-
}
|
|
128
|
-
// --- 获取 to 的 scheme, host, path ---
|
|
129
|
-
const t = parseUrl(to);
|
|
130
|
-
// --- 已经是绝对路径,直接返回 ---
|
|
131
|
-
if (t.protocol) {
|
|
132
|
-
// --- 获取小写的 protocol ---
|
|
133
|
-
return urlAtom(t.protocol + to.slice(t.protocol.length));
|
|
134
|
-
}
|
|
135
|
-
// --- # 或 ? 替换后返回 ---
|
|
136
|
-
if (to.startsWith('#') || to.startsWith('?')) {
|
|
137
|
-
const sp = from.indexOf(to[0]);
|
|
138
|
-
if (sp !== -1) {
|
|
139
|
-
return urlAtom(from.slice(0, sp) + to);
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
return urlAtom(from + to);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
// --- 处理后面的尾随路径 ---
|
|
146
|
-
let abs = (f.auth ? f.auth + '@' : '') + (f.host ?? '');
|
|
147
|
-
if (to.startsWith('/')) {
|
|
148
|
-
// -- abs 类似是 /xx/xx ---
|
|
149
|
-
abs += to;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
// --- to 是 xx/xx 这样的 ---
|
|
153
|
-
// --- 移除基准 path 不是路径的部分,如 /ab/c 变成了 /ab,/ab 变成了 空 ---
|
|
154
|
-
const path = f.pathname.replace(/\/[^/]*$/g, '');
|
|
155
|
-
// --- abs 是 /xx/xx 了,因为如果 path 是空,则跟上了 /,如果 path 不为空,也是 / 开头 ---
|
|
156
|
-
abs += path + '/' + to;
|
|
157
|
-
}
|
|
158
|
-
// --- 返回最终结果 ---
|
|
159
|
-
if (f.protocol && (f.protocol !== 'file:') && !f.host) {
|
|
160
|
-
// --- 类似 c:/ ---
|
|
161
|
-
return urlAtom(f.protocol + abs);
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
// --- 类似 http:// ---
|
|
165
|
-
return urlAtom((f.protocol ? f.protocol + '//' : '') + abs);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* --- 将路径中的 ../ ./ 都按规范妥善处理 ---
|
|
171
|
-
* @param url 要处理的地址
|
|
172
|
-
*/
|
|
173
|
-
export function urlAtom(url: string): string {
|
|
174
|
-
// --- 删掉 ./ ---
|
|
175
|
-
while (url.includes('/./')) {
|
|
176
|
-
url = url.replace(/\/\.\//g, '/');
|
|
177
|
-
}
|
|
178
|
-
// --- 删掉 ../ ---
|
|
179
|
-
while (/\/(?!\.\.)[^/]+\/\.\.\//.test(url)) {
|
|
180
|
-
url = url.replace(/\/(?!\.\.)[^/]+\/\.\.\//g, '/');
|
|
181
|
-
}
|
|
182
|
-
url = url.replace(/\.\.\//g, '');
|
|
183
|
-
return url;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
export const REGEXP_EMAIL = /^[-_\w.]+@[-_\w.]+\.([a-zA-Z]+)$/i;
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* --- 是否是邮件地址 ---
|
|
190
|
-
* @param email
|
|
191
|
-
*/
|
|
192
|
-
export function isEMail(email: string): boolean {
|
|
193
|
-
return REGEXP_EMAIL.test(email);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export const REGEXP_IPV4 = /^[0-9]{1,3}(\.[0-9]{1,3}){3}$/i;
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* --- 是否是 IPv4 ---
|
|
200
|
-
* @param ip
|
|
201
|
-
*/
|
|
202
|
-
export function isIPv4(ip: string): boolean {
|
|
203
|
-
return REGEXP_IPV4.test(ip);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export const REGEXP_IPV6 = /^(\w*?:){2,7}[\w.]*$/i;
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* --- 是否是 IPv6 ---
|
|
210
|
-
* @param ip
|
|
211
|
-
*/
|
|
212
|
-
export function isIPv6(ip: string): boolean {
|
|
213
|
-
return REGEXP_IPV6.test(ip + ':');
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export const REGEXP_DOMAIN = /^.+?\.((?![0-9]).)+$/i;
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* --- 判断是否是域名 ---
|
|
220
|
-
* @param string $domain
|
|
221
|
-
* @return bool
|
|
222
|
-
*/
|
|
223
|
-
export function isDomain(domain: string): boolean {
|
|
224
|
-
return REGEXP_DOMAIN.test(domain);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export const REGEXP_ASCII = /^[\x20-\x7E]*$/;
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* --- 判断是否在 ascii 字符集内,仅可输入部分 ---
|
|
231
|
-
* @param text 要判断的文本
|
|
232
|
-
*/
|
|
233
|
-
export function isAscii(text: string): boolean {
|
|
234
|
-
return REGEXP_ASCII.test(text);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* --- 换行替换为别的 ---
|
|
239
|
-
* @param str 要替换的字符串
|
|
240
|
-
* @param to 换行替换符
|
|
241
|
-
*/
|
|
242
|
-
export function nlReplace(str: string, to: string = '\n'): string {
|
|
243
|
-
str = str.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
244
|
-
if (to !== '\n') {
|
|
245
|
-
str = str.replace(/\n/g, to);
|
|
246
|
-
}
|
|
247
|
-
return str;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/** Tld 列表 */
|
|
251
|
-
let tldList: string[];
|
|
252
|
-
|
|
253
|
-
/** 解析后的 domain */
|
|
254
|
-
export interface IDomain {
|
|
255
|
-
tld: string | null;
|
|
256
|
-
sld: string | null;
|
|
257
|
-
domain: string | null;
|
|
258
|
-
sub: string | null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* --- 解析域名并获取 tld/sld/domain/sub ---
|
|
263
|
-
* @param domain 域名
|
|
264
|
-
*/
|
|
265
|
-
export async function parseDomain(domain: string): Promise<IDomain> {
|
|
266
|
-
const rtn: IDomain = {
|
|
267
|
-
tld: null,
|
|
268
|
-
sld: null,
|
|
269
|
-
domain: null,
|
|
270
|
-
sub: null
|
|
271
|
-
};
|
|
272
|
-
if (!isDomain(domain)) {
|
|
273
|
-
return rtn;
|
|
274
|
-
}
|
|
275
|
-
const arr = domain.split('.');
|
|
276
|
-
if (arr.length === 1) {
|
|
277
|
-
rtn.tld = arr[0].toLowerCase();
|
|
278
|
-
rtn.domain = rtn.tld;
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
if (!tldList) {
|
|
282
|
-
tldList = JSON.parse(await fs.getContent(kebab.LIB_PATH + 'text/tld.json', 'utf8') ?? '[]');
|
|
283
|
-
}
|
|
284
|
-
const last2 = (arr[arr.length - 2] + '.' + arr[arr.length - 1]).toLowerCase();
|
|
285
|
-
if (tldList.includes(last2)) {
|
|
286
|
-
// --- last2 就是 tld ---
|
|
287
|
-
rtn.tld = last2;
|
|
288
|
-
if (arr.length === 2) {
|
|
289
|
-
// --- 没有 sld ---
|
|
290
|
-
rtn.domain = last2;
|
|
291
|
-
return rtn;
|
|
292
|
-
}
|
|
293
|
-
rtn.sld = arr[arr.length - 3].toLowerCase();
|
|
294
|
-
rtn.domain = rtn.sld + '.' + rtn.tld;
|
|
295
|
-
// --- 判断是否有 sub ---
|
|
296
|
-
if (arr.length === 3) {
|
|
297
|
-
return rtn;
|
|
298
|
-
}
|
|
299
|
-
arr.splice(-3);
|
|
300
|
-
rtn.sub = arr.join('.').toLowerCase();
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
303
|
-
rtn.tld = arr[arr.length - 1].toLowerCase();
|
|
304
|
-
rtn.sld = arr[arr.length - 2].toLowerCase();
|
|
305
|
-
rtn.domain = rtn.sld + '.' + rtn.tld;
|
|
306
|
-
// --- 判断是否有 sub ---
|
|
307
|
-
if (arr.length === 2) {
|
|
308
|
-
return rtn;
|
|
309
|
-
}
|
|
310
|
-
arr.splice(-2);
|
|
311
|
-
rtn.sub = arr.join('.').toLowerCase();
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return rtn;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* --- 传入正则进行匹配 str 是否有一项满足 ---
|
|
319
|
-
* @param str 要检测的字符串
|
|
320
|
-
* @param regs 正则列表
|
|
321
|
-
*/
|
|
322
|
-
export function match(str: string, regs: RegExp[]): boolean {
|
|
323
|
-
for (const reg of regs) {
|
|
324
|
-
if (reg.test(str)) {
|
|
325
|
-
return true;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// --- 以下是适用于中国大陆的方法 ---
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* --- 判断手机号是否是 11 位,不做真实性校验 ---
|
|
335
|
-
* @param p 手机号
|
|
336
|
-
*/
|
|
337
|
-
export function isPhoneCN(p: string): boolean {
|
|
338
|
-
return /^1[0-9]{10}$/.test(p);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* --- 是否是中国大陆身份证号码 ---
|
|
343
|
-
* @param idcard 身份证号
|
|
344
|
-
*/
|
|
345
|
-
export function isIdCardCN(idcard: string): boolean {
|
|
346
|
-
if (idcard.length !== 18) {
|
|
347
|
-
return false;
|
|
348
|
-
}
|
|
349
|
-
// --- 取出本码 ---
|
|
350
|
-
const idcardBase = idcard.slice(0, 17);
|
|
351
|
-
// --- 取出校验码 ---
|
|
352
|
-
const verifyCode = idcard.slice(17, 18);
|
|
353
|
-
// --- 加权因子 ---
|
|
354
|
-
const factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
|
355
|
-
// --- 校验码对应值 ---
|
|
356
|
-
const verifyCodeList = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
|
357
|
-
// --- 根据前17位计算校验码 ---
|
|
358
|
-
let total = BigInt(0);
|
|
359
|
-
for (let i = 0; i < 17; i++) {
|
|
360
|
-
total += BigInt(idcardBase.slice(i, i + 1)) * BigInt(factor[i]);
|
|
361
|
-
}
|
|
362
|
-
// --- 取模 ---
|
|
363
|
-
const mod = total % BigInt(11);
|
|
364
|
-
// --- 比较校验码 ---
|
|
365
|
-
if (verifyCode === verifyCodeList[Number(mod)]) {
|
|
366
|
-
return true;
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
return false;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// --- 以下 Mutton: false, Kebab: true ---
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* --- 将对象转换为 query string ---
|
|
377
|
-
* @param query 要转换的对象
|
|
378
|
-
* @param encode 是否转义
|
|
379
|
-
*/
|
|
380
|
-
export function queryStringify(query: Record<string, any>, encode: boolean = true): string {
|
|
381
|
-
if (encode) {
|
|
382
|
-
return Object.entries(query).map(([k, v]) => {
|
|
383
|
-
if (Array.isArray(v)) {
|
|
384
|
-
return v.map((i) => `${encodeURIComponent(k)}=${encodeURIComponent(i)}`).join('&');
|
|
385
|
-
}
|
|
386
|
-
return `${encodeURIComponent(k)}=${encodeURIComponent(v)}`;
|
|
387
|
-
}).join('&');
|
|
388
|
-
}
|
|
389
|
-
return Object.entries(query).map(([k, v]) => {
|
|
390
|
-
if (Array.isArray(v)) {
|
|
391
|
-
return v.map((i) => `${k}=${i}}`).join('&');
|
|
392
|
-
}
|
|
393
|
-
return `${k}=${v}`;
|
|
394
|
-
}).join('&');
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* --- 将 query string 转换为对象 ---
|
|
399
|
-
* @param query 要转换的字符串
|
|
400
|
-
*/
|
|
401
|
-
export function queryParse(query: string): Record<string, string | string[]> {
|
|
402
|
-
const ret: Record<string, string | string[]> = {};
|
|
403
|
-
const arrayKeys: Record<string, boolean> = {};
|
|
404
|
-
const arr = query.split('&');
|
|
405
|
-
for (const i of arr) {
|
|
406
|
-
if (!i.length) {
|
|
407
|
-
continue;
|
|
408
|
-
}
|
|
409
|
-
const pos = i.indexOf('=');
|
|
410
|
-
|
|
411
|
-
const key = decodeURIComponent(pos === -1 ? i : i.slice(0, pos));
|
|
412
|
-
let value = '';
|
|
413
|
-
try {
|
|
414
|
-
value = pos === -1 ? '' : decodeURIComponent(i.slice(pos + 1));
|
|
415
|
-
}
|
|
416
|
-
catch {
|
|
417
|
-
value = pos === -1 ? '' : i.slice(pos + 1);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (arrayKeys[key]) {
|
|
421
|
-
(ret[key] as string[]).push(value);
|
|
422
|
-
}
|
|
423
|
-
else if (undefined === ret[key]) {
|
|
424
|
-
ret[key] = value;
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
ret[key] = [ret[key] as string, value];
|
|
428
|
-
arrayKeys[key] = true;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
return ret;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* --- HTML 特殊字符转换为实体字符 ---
|
|
436
|
-
* @param html 待转换的 HTML
|
|
437
|
-
*/
|
|
438
|
-
export function htmlescape(html: string): string {
|
|
439
|
-
const type = typeof html;
|
|
440
|
-
if (type !== 'string') {
|
|
441
|
-
return '[' + type + ']';
|
|
442
|
-
}
|
|
443
|
-
return html.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/'/g, '"');
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* --- 判断是否是绝对路径,是返回 true,相对路径返回 false ---
|
|
448
|
-
* @param path 要判断的路径字符串
|
|
449
|
-
*/
|
|
450
|
-
export function isRealPath(path: string): boolean {
|
|
451
|
-
path = path.replace(/\\/g, '/');
|
|
452
|
-
if (path.startsWith('/')) {
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
|
-
return /[a-z]+:/i.test(path.split('/')[0]) ? true : false;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* --- 获取文件名 ---
|
|
460
|
-
* @param path 文件路径
|
|
461
|
-
*/
|
|
462
|
-
export function getFilename(path: string): string {
|
|
463
|
-
path = path.replace(/\\/g, '/');
|
|
464
|
-
const lio = path.lastIndexOf('/');
|
|
465
|
-
if (lio === -1) {
|
|
466
|
-
return path;
|
|
467
|
-
}
|
|
468
|
-
return path.slice(lio + 1);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* --- 将普通的返回 JSON 对象序列化为字符串,Mutton 不能使用 ---
|
|
473
|
-
* @param o 返回 JSON 对象
|
|
474
|
-
*/
|
|
475
|
-
export function stringifyResult(rtn: types.Json): string {
|
|
476
|
-
if (Array.isArray(rtn)) {
|
|
477
|
-
// --- [0, 'xxx'] 模式 ---
|
|
478
|
-
if (rtn.length === 0) {
|
|
479
|
-
return JSON.stringify({
|
|
480
|
-
'result': 0,
|
|
481
|
-
'msg': 'ERROR'
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
if (typeof rtn[0] === 'number') {
|
|
485
|
-
// --- 1. ---
|
|
486
|
-
const json: Record<string, types.Json> = { 'result': rtn[0] };
|
|
487
|
-
if (rtn[1] !== undefined) {
|
|
488
|
-
if (typeof rtn[1] === 'object') {
|
|
489
|
-
// --- [0, ...{'xx': 'xx'}] ---
|
|
490
|
-
for (let i = 1; i < rtn.length; ++i) {
|
|
491
|
-
Object.assign(json, rtn[i]);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
// --- [0, 'xxx'], [0, 'xxx', ...{'xx': 'xx'}] ---
|
|
496
|
-
json['msg'] = rtn[1];
|
|
497
|
-
for (let i = 2; i < rtn.length; ++i) {
|
|
498
|
-
Object.assign(json, rtn[i]);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
return JSON.stringify(json);
|
|
503
|
-
}
|
|
504
|
-
// --- 直接是个 json 对象 ---
|
|
505
|
-
return JSON.stringify(rtn);
|
|
506
|
-
}
|
|
507
|
-
// --- 直接是个 json 对象 ---
|
|
508
|
-
return JSON.stringify(rtn);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* --- 将字符串解析为对象,返回 false 代表解析失败,支持 BigInt,Kebab true, Mutton false ---
|
|
513
|
-
* @param str 要解析的 json 字符串
|
|
514
|
-
*/
|
|
515
|
-
export function parseJson(str: string): any {
|
|
516
|
-
try {
|
|
517
|
-
str = str.replace(/("[\w-]+?" *: *)([-+0-9]+)([ \r\n]*[,}]|$)/g, (v, v1, v2, v3) => {
|
|
518
|
-
return v1 + '"-mybigint-' + v2 + '"' + v3;
|
|
519
|
-
});
|
|
520
|
-
return JSON.parse(str, (k, v) => {
|
|
521
|
-
if (typeof v !== 'string') {
|
|
522
|
-
return v;
|
|
523
|
-
}
|
|
524
|
-
if (!v.startsWith('-mybigint-')) {
|
|
525
|
-
return v;
|
|
526
|
-
}
|
|
527
|
-
const ints = v.slice(10);
|
|
528
|
-
const int = parseInt(ints);
|
|
529
|
-
if (int <= Number.MAX_SAFE_INTEGER) {
|
|
530
|
-
return int;
|
|
531
|
-
}
|
|
532
|
-
return BigInt(ints);
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
return false;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* --- 将对象转换为 json 字符串,返回 false 代表解析失败,支持 BigInt,Kebab true, Mutton false ---
|
|
542
|
-
* @param obj 要转换的 json 对象
|
|
543
|
-
* @param space 美化方式
|
|
544
|
-
*/
|
|
545
|
-
export function stringifyJson(obj: types.Json, space?: string | number): string {
|
|
546
|
-
return JSON.stringify(obj, (k, v) => {
|
|
547
|
-
if (typeof v === 'bigint') {
|
|
548
|
-
return '-mybigint-' + v.toString();
|
|
549
|
-
}
|
|
550
|
-
return v;
|
|
551
|
-
}, space).replace(/"-mybigint-([-+0-9]+?)"/g, '$1');
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* --- 判断一个值是否是虚假的(为 null/undefined/空字符串/false/0 等) ---
|
|
556
|
-
* @param val 要判断的值
|
|
557
|
-
*/
|
|
558
|
-
export function isFalsy(val: any): boolean {
|
|
559
|
-
return (val === null || val === undefined || val === '' || val === false || val === 0) ? true : false;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* --- 为解决精度问题,将字符串数字转换为整数显示 ---
|
|
564
|
-
* --- 以下几个示例都是当 digits 为 2 时 ---
|
|
565
|
-
* --- str 传入 '1.234',返回 123 ---
|
|
566
|
-
* --- str 传入 '1.235',返回 124 ---
|
|
567
|
-
* --- str 传入 '1.1',返回 110 ---
|
|
568
|
-
* --- str 传入 '6',返回 600 ---
|
|
569
|
-
* @param str 要转换的数字字符串
|
|
570
|
-
* @param digits 小数点右移位数
|
|
571
|
-
*/
|
|
572
|
-
export function str2int(str: string, digits: number = 3): number {
|
|
573
|
-
const num = parseFloat(str);
|
|
574
|
-
/** --- 位数,如 1000 --- */
|
|
575
|
-
const factor = Math.pow(10, digits);
|
|
576
|
-
return Math.round(num * factor);
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/**
|
|
580
|
-
* --- 为解决精度问题,将整数转换为小数字符串 ---
|
|
581
|
-
* --- 以下几个示例都是当 digits 为 3、decimal 为 2 时 ---
|
|
582
|
-
* --- int 传入 2341,返回 '2.34' ---
|
|
583
|
-
* --- int 传入 2345,返回 '2.35' ---
|
|
584
|
-
* --- int 传入 23,返回 '0.02' ---
|
|
585
|
-
* --- int 传入 2,返回 '0.00' ---
|
|
586
|
-
* @param int 要转换的整数
|
|
587
|
-
* @param digits 小数点左移位数
|
|
588
|
-
* @param decimal 最终保留的小数位数
|
|
589
|
-
*/
|
|
590
|
-
export function int2str(int: number, digits: number = 4, decimal: number = 2): string {
|
|
591
|
-
/** --- 正负符号 --- */
|
|
592
|
-
const sign = int < 0 ? '-' : '';
|
|
593
|
-
const absInt = Math.abs(int);
|
|
594
|
-
let intStr = absInt.toString();
|
|
595
|
-
// --- 补前导零至长度 digits + 1 ---
|
|
596
|
-
while (intStr.length < digits + 1) {
|
|
597
|
-
intStr = '0' + intStr;
|
|
598
|
-
}
|
|
599
|
-
/** --- 整数部分字符串 --- */
|
|
600
|
-
const intPart = intStr.slice(0, intStr.length - digits);
|
|
601
|
-
/** --- 小数部分字符串 --- */
|
|
602
|
-
const decPart = intStr.slice(intStr.length - digits);
|
|
603
|
-
/** --- 小数部分数字 --- */
|
|
604
|
-
const decNum = parseInt(decPart);
|
|
605
|
-
/** --- 要除的数 --- */
|
|
606
|
-
const div = Math.pow(10, digits - decimal);
|
|
607
|
-
const round = Math.round(decNum / div);
|
|
608
|
-
const carry = Math.floor(round / Math.pow(10, decimal));
|
|
609
|
-
const newDecPart = round % Math.pow(10, decimal);
|
|
610
|
-
|
|
611
|
-
const intNum = parseInt(intPart, 10) + carry;
|
|
612
|
-
const decResult = newDecPart.toString().padStart(decimal, '0');
|
|
613
|
-
|
|
614
|
-
return `${sign}${intNum}${decResult ? `.${decResult}` : ''}`;
|
|
615
|
-
}
|