@zhin.js/core 1.0.16 → 1.0.18
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/CHANGELOG.md +19 -0
- package/REFACTORING_COMPLETE.md +178 -0
- package/REFACTORING_STATUS.md +263 -0
- package/lib/adapter.d.ts +44 -19
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +81 -50
- package/lib/adapter.js.map +1 -1
- package/lib/bot.d.ts +7 -12
- package/lib/bot.d.ts.map +1 -1
- package/lib/built/adapter-process.d.ts +36 -0
- package/lib/built/adapter-process.d.ts.map +1 -0
- package/lib/built/adapter-process.js +77 -0
- package/lib/built/adapter-process.js.map +1 -0
- package/lib/built/command.d.ts +46 -0
- package/lib/built/command.d.ts.map +1 -0
- package/lib/built/command.js +54 -0
- package/lib/built/command.js.map +1 -0
- package/lib/built/component.d.ts +42 -0
- package/lib/built/component.d.ts.map +1 -0
- package/lib/built/component.js +66 -0
- package/lib/built/component.js.map +1 -0
- package/lib/built/config.d.ts +31 -0
- package/lib/built/config.d.ts.map +1 -0
- package/lib/built/config.js +141 -0
- package/lib/built/config.js.map +1 -0
- package/lib/built/cron.d.ts +53 -0
- package/lib/built/cron.d.ts.map +1 -0
- package/lib/built/cron.js +79 -0
- package/lib/built/cron.js.map +1 -0
- package/lib/built/database.d.ts +17 -0
- package/lib/built/database.d.ts.map +1 -0
- package/lib/built/database.js +28 -0
- package/lib/built/database.js.map +1 -0
- package/lib/{permissions.d.ts → built/permission.d.ts} +5 -10
- package/lib/built/permission.d.ts.map +1 -0
- package/lib/{permissions.js → built/permission.js} +11 -10
- package/lib/built/permission.js.map +1 -0
- package/lib/command.d.ts +18 -7
- package/lib/command.d.ts.map +1 -1
- package/lib/command.js +36 -15
- package/lib/command.js.map +1 -1
- package/lib/component.d.ts +1 -1
- package/lib/component.d.ts.map +1 -1
- package/lib/component.js.map +1 -1
- package/lib/cron.d.ts +4 -12
- package/lib/cron.d.ts.map +1 -1
- package/lib/cron.js +33 -64
- package/lib/cron.js.map +1 -1
- package/lib/index.d.ts +11 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +14 -4
- package/lib/index.js.map +1 -1
- package/lib/jsx-runtime.d.ts +2 -2
- package/lib/jsx.d.ts +2 -3
- package/lib/jsx.d.ts.map +1 -1
- package/lib/jsx.js.map +1 -1
- package/lib/message.d.ts +4 -7
- package/lib/message.d.ts.map +1 -1
- package/lib/message.js.map +1 -1
- package/lib/plugin.d.ts +164 -51
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +520 -137
- package/lib/plugin.js.map +1 -1
- package/lib/prompt.d.ts +1 -1
- package/lib/prompt.d.ts.map +1 -1
- package/lib/prompt.js +2 -1
- package/lib/prompt.js.map +1 -1
- package/lib/types.d.ts +33 -33
- package/lib/types.d.ts.map +1 -1
- package/lib/utils.d.ts +16 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +166 -66
- package/lib/utils.js.map +1 -1
- package/package.json +17 -11
- package/src/adapter.ts +131 -80
- package/src/bot.ts +8 -13
- package/src/built/adapter-process.ts +77 -0
- package/src/built/command.ts +102 -0
- package/src/built/component.ts +111 -0
- package/src/built/config.ts +126 -0
- package/src/built/cron.ts +140 -0
- package/src/built/database.ts +38 -0
- package/src/{permissions.ts → built/permission.ts} +9 -12
- package/src/command.ts +48 -20
- package/src/component.ts +2 -3
- package/src/cron.ts +35 -70
- package/src/index.ts +15 -5
- package/src/jsx.ts +2 -3
- package/src/message.ts +3 -4
- package/src/plugin.ts +671 -184
- package/src/prompt.ts +4 -3
- package/src/types.ts +41 -35
- package/src/utils.ts +418 -296
- package/test/minimal-bot.ts +31 -0
- package/test/stress-test.ts +123 -0
- package/tests/command.test.ts +124 -44
- package/ASYNC-JSX-SUPPORT.md +0 -173
- package/lib/app.d.ts +0 -191
- package/lib/app.d.ts.map +0 -1
- package/lib/app.js +0 -604
- package/lib/app.js.map +0 -1
- package/lib/config.d.ts +0 -54
- package/lib/config.d.ts.map +0 -1
- package/lib/config.js +0 -308
- package/lib/config.js.map +0 -1
- package/lib/log-transport.d.ts +0 -37
- package/lib/log-transport.d.ts.map +0 -1
- package/lib/log-transport.js +0 -136
- package/lib/log-transport.js.map +0 -1
- package/lib/permissions.d.ts.map +0 -1
- package/lib/permissions.js.map +0 -1
- package/src/app.ts +0 -772
- package/src/config.ts +0 -397
- package/src/log-transport.ts +0 -163
- package/tests/app.test.ts +0 -265
- package/tests/permissions.test.ts +0 -358
- package/tests/plugin.test.ts +0 -234
- package/tests/prompt.test.ts +0 -223
package/src/utils.ts
CHANGED
|
@@ -1,351 +1,473 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import {
|
|
4
|
+
AdapterMessage,
|
|
5
|
+
Dict,
|
|
6
|
+
MessageElement,
|
|
7
|
+
MessageMiddleware,
|
|
8
|
+
RegisteredAdapter,
|
|
9
|
+
SendContent,
|
|
10
|
+
} from "./types";
|
|
2
11
|
import { Message } from "./message.js";
|
|
3
12
|
|
|
4
13
|
export function getValueWithRuntime(template: string, ctx: Dict) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
14
|
+
const result = evaluate(template, ctx);
|
|
15
|
+
if (result === `return(${template})`) return undefined;
|
|
16
|
+
return result;
|
|
8
17
|
}
|
|
9
18
|
export const evaluate = <S, T = any>(exp: string, context: S) => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
const result = execute<S, T>(`return(${exp})`, context);
|
|
20
|
+
// 如果结果是原始表达式,说明访问被阻止,返回 undefined
|
|
21
|
+
if (result === `return(${exp})`) return undefined;
|
|
22
|
+
return result;
|
|
14
23
|
};
|
|
15
24
|
/**
|
|
16
25
|
* 组合中间件,洋葱模型
|
|
26
|
+
* 灵感来源于 zhinjs/next 的 Hooks.compose
|
|
27
|
+
*
|
|
17
28
|
* @param middlewares 中间件列表
|
|
18
29
|
* @returns 中间件处理函数
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const composed = compose([middleware1, middleware2]);
|
|
34
|
+
* await composed(message);
|
|
35
|
+
* ```
|
|
19
36
|
*/
|
|
20
|
-
export function compose<P extends RegisteredAdapter>(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
export function compose<P extends RegisteredAdapter=RegisteredAdapter>(
|
|
38
|
+
middlewares: MessageMiddleware<P>[]
|
|
39
|
+
) {
|
|
40
|
+
// 性能优化:空数组直接返回空函数
|
|
41
|
+
if (middlewares.length === 0) {
|
|
42
|
+
return () => Promise.resolve();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 性能优化:单个中间件直接返回
|
|
46
|
+
if (middlewares.length === 1) {
|
|
47
|
+
return (message: Message<AdapterMessage<P>>, next: () => Promise<void> = () => Promise.resolve()) => {
|
|
48
|
+
return middlewares[0](message, next);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return function (
|
|
53
|
+
message: Message<AdapterMessage<P>>,
|
|
54
|
+
next: () => Promise<void>
|
|
55
|
+
) {
|
|
56
|
+
let index = -1;
|
|
57
|
+
const dispatch = async (i: number = 0): Promise<void> => {
|
|
58
|
+
// 防止 next() 被多次调用
|
|
59
|
+
if (i <= index) {
|
|
60
|
+
return Promise.reject(new Error("next() called multiple times"));
|
|
61
|
+
}
|
|
62
|
+
index = i;
|
|
63
|
+
let fn = middlewares[i];
|
|
64
|
+
if (i === middlewares.length) fn = next;
|
|
65
|
+
if (!fn) return;
|
|
66
|
+
try {
|
|
67
|
+
return await fn(message, () => dispatch(i + 1));
|
|
68
|
+
} catch (error) {
|
|
69
|
+
// 中间件异常应该被记录但不中断整个流程
|
|
70
|
+
console.error("Middleware error:", error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
return dispatch(0);
|
|
75
|
+
};
|
|
39
76
|
}
|
|
77
|
+
// 使用 LRU 缓存限制大小,防止内存泄漏
|
|
78
|
+
const MAX_EVAL_CACHE_SIZE = 1000;
|
|
40
79
|
const evalCache: Record<string, Function> = Object.create(null);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
uptime:process.uptime(),
|
|
52
|
-
memoryUsage:process.memoryUsage(),
|
|
53
|
-
cpuUsage:process.cpuUsage(),
|
|
54
|
-
pid:process.pid,
|
|
55
|
-
ppid:process.ppid,
|
|
56
|
-
},
|
|
57
|
-
Bun:'你想干嘛',
|
|
58
|
-
global:undefined,
|
|
59
|
-
Buffer:undefined,
|
|
60
|
-
crypto:undefined
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
return fn.apply(context, [context]);
|
|
64
|
-
} catch {
|
|
65
|
-
return exp as T;
|
|
80
|
+
const evalCacheKeys: string[] = [];
|
|
81
|
+
|
|
82
|
+
export const execute = <S, T = any>(exp: string, context: S): T => {
|
|
83
|
+
let fn = evalCache[exp];
|
|
84
|
+
|
|
85
|
+
if (!fn) {
|
|
86
|
+
// 如果缓存已满,删除最旧的条目(LRU)
|
|
87
|
+
if (evalCacheKeys.length >= MAX_EVAL_CACHE_SIZE) {
|
|
88
|
+
const oldest = evalCacheKeys.shift()!;
|
|
89
|
+
delete evalCache[oldest];
|
|
66
90
|
}
|
|
91
|
+
|
|
92
|
+
fn = evalCache[exp] = toFunction(exp);
|
|
93
|
+
evalCacheKeys.push(exp);
|
|
94
|
+
}
|
|
95
|
+
context = {
|
|
96
|
+
...context,
|
|
97
|
+
process: {
|
|
98
|
+
version: process.version,
|
|
99
|
+
versions: process.versions,
|
|
100
|
+
platform: process.platform,
|
|
101
|
+
arch: process.arch,
|
|
102
|
+
release: process.release,
|
|
103
|
+
uptime: process.uptime(),
|
|
104
|
+
memoryUsage: process.memoryUsage(),
|
|
105
|
+
cpuUsage: process.cpuUsage(),
|
|
106
|
+
pid: process.pid,
|
|
107
|
+
ppid: process.ppid,
|
|
108
|
+
},
|
|
109
|
+
Bun: "你想干嘛",
|
|
110
|
+
global: undefined,
|
|
111
|
+
Buffer: undefined,
|
|
112
|
+
crypto: undefined,
|
|
113
|
+
};
|
|
114
|
+
try {
|
|
115
|
+
return fn.apply(context, [context]);
|
|
116
|
+
} catch {
|
|
117
|
+
return exp as T;
|
|
118
|
+
}
|
|
67
119
|
};
|
|
68
120
|
|
|
69
121
|
const toFunction = (exp: string): Function => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
122
|
+
try {
|
|
123
|
+
return new Function(`$data`, `with($data){${exp}}`);
|
|
124
|
+
} catch {
|
|
125
|
+
return () => { };
|
|
126
|
+
}
|
|
75
127
|
};
|
|
128
|
+
|
|
129
|
+
// 清理 evalCache(用于内存调试)
|
|
130
|
+
export function clearEvalCache(): void {
|
|
131
|
+
Object.keys(evalCache).forEach(key => {
|
|
132
|
+
delete evalCache[key];
|
|
133
|
+
});
|
|
134
|
+
evalCacheKeys.length = 0;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 获取 evalCache 统计信息(用于内存调试)
|
|
138
|
+
export function getEvalCacheStats(): { size: number; maxSize: number } {
|
|
139
|
+
return {
|
|
140
|
+
size: evalCacheKeys.length,
|
|
141
|
+
maxSize: MAX_EVAL_CACHE_SIZE
|
|
142
|
+
};
|
|
143
|
+
}
|
|
76
144
|
export function compiler(template: string, ctx: Dict) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
145
|
+
const matched = [...template.matchAll(/\${([^}]*?)}/g)];
|
|
146
|
+
for (const item of matched) {
|
|
147
|
+
const tpl = item[1];
|
|
148
|
+
let value = getValueWithRuntime(tpl, ctx);
|
|
149
|
+
if (value === tpl) continue;
|
|
150
|
+
if (typeof value !== "string") value = JSON.stringify(value, null, 2);
|
|
151
|
+
template = template.replace(`\${${item[1]}}`, value);
|
|
152
|
+
}
|
|
153
|
+
return template;
|
|
86
154
|
}
|
|
87
|
-
export function segment<T extends object>(type:string,data:T){
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
155
|
+
export function segment<T extends object>(type: string, data: T) {
|
|
156
|
+
return {
|
|
157
|
+
type,
|
|
158
|
+
data,
|
|
159
|
+
};
|
|
92
160
|
}
|
|
93
|
-
export namespace segment{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
result.push({
|
|
156
|
-
type: type,
|
|
157
|
-
data,
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
if (template.length) {
|
|
161
|
-
result.push({
|
|
162
|
-
type: 'text',
|
|
163
|
-
data: {
|
|
164
|
-
text: unescape(template),
|
|
165
|
-
},
|
|
166
|
-
});
|
|
161
|
+
export namespace segment {
|
|
162
|
+
export function escape<T>(text: T): T {
|
|
163
|
+
if (typeof text !== "string") return text;
|
|
164
|
+
return text
|
|
165
|
+
.replace(/&/g, "&")
|
|
166
|
+
.replace(/</g, "<")
|
|
167
|
+
.replace(/>/g, ">")
|
|
168
|
+
.replace(/"/g, """)
|
|
169
|
+
.replace(/'/g, "'") as T;
|
|
170
|
+
}
|
|
171
|
+
export function unescape<T>(text: T): T {
|
|
172
|
+
if (typeof text !== "string") return text;
|
|
173
|
+
return text
|
|
174
|
+
.replace(/</g, "<")
|
|
175
|
+
.replace(/>/g, ">")
|
|
176
|
+
.replace(/"/g, '"')
|
|
177
|
+
.replace(/'/g, "'")
|
|
178
|
+
.replace(/&/g, "&") as T;
|
|
179
|
+
}
|
|
180
|
+
export function text(text: string) {
|
|
181
|
+
return segment("text", { text });
|
|
182
|
+
}
|
|
183
|
+
export function face(id: string, text?: string) {
|
|
184
|
+
return segment("face", { id, text });
|
|
185
|
+
}
|
|
186
|
+
export function from(content: SendContent): SendContent {
|
|
187
|
+
if (!Array.isArray(content)) content = [content];
|
|
188
|
+
const toString = (template: string | MessageElement) => {
|
|
189
|
+
if (typeof template !== "string") return [template];
|
|
190
|
+
template = unescape(template);
|
|
191
|
+
const result: MessageElement[] = [];
|
|
192
|
+
const closingReg = /<(\S+)(\s[^>]+)?\/>/;
|
|
193
|
+
const twinningReg = /<(\S+)(\s[^>]+)?>([\s\S]*?)<\/\1>/;
|
|
194
|
+
while (template.length) {
|
|
195
|
+
const [_, type, attrStr = "", child = ""] =
|
|
196
|
+
template.match(twinningReg) || template.match(closingReg) || [];
|
|
197
|
+
if (!type) break;
|
|
198
|
+
const isClosing = closingReg.test(template);
|
|
199
|
+
const matched = isClosing
|
|
200
|
+
? `<${type}${attrStr}/>`
|
|
201
|
+
: `<${type}${attrStr}>${child}</${type}>`;
|
|
202
|
+
const index = template.indexOf(matched);
|
|
203
|
+
const prevText = template.slice(0, index);
|
|
204
|
+
if (prevText)
|
|
205
|
+
result.push({
|
|
206
|
+
type: "text",
|
|
207
|
+
data: {
|
|
208
|
+
text: unescape(prevText),
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
template = template.slice(index + matched.length);
|
|
212
|
+
const attrArr = [
|
|
213
|
+
...attrStr.matchAll(/\s([^=]+)(?=(?=="([^"]+)")|(?=='([^']+)'))/g),
|
|
214
|
+
];
|
|
215
|
+
const data = Object.fromEntries(
|
|
216
|
+
attrArr.map(([source, key, v1, v2]) => {
|
|
217
|
+
const value = v1 || v2;
|
|
218
|
+
try {
|
|
219
|
+
return [key, JSON.parse(unescape(value))];
|
|
220
|
+
} catch {
|
|
221
|
+
return [key, unescape(value)];
|
|
167
222
|
}
|
|
168
|
-
|
|
223
|
+
})
|
|
224
|
+
);
|
|
225
|
+
if (child) {
|
|
226
|
+
data.message = toString(child).map(({ type, data }) => ({
|
|
227
|
+
type,
|
|
228
|
+
...data,
|
|
229
|
+
}));
|
|
169
230
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
231
|
+
result.push({
|
|
232
|
+
type: type,
|
|
233
|
+
data,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (template.length) {
|
|
237
|
+
result.push({
|
|
238
|
+
type: "text",
|
|
239
|
+
data: {
|
|
240
|
+
text: unescape(template),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
245
|
+
};
|
|
246
|
+
return content.reduce((result, item) => {
|
|
247
|
+
result.push(...toString(item));
|
|
248
|
+
return result;
|
|
249
|
+
}, [] as MessageElement[]);
|
|
250
|
+
}
|
|
251
|
+
export function raw(content: SendContent) {
|
|
252
|
+
if (!Array.isArray(content)) content = [content];
|
|
253
|
+
return content
|
|
254
|
+
.map((item) => {
|
|
255
|
+
if (typeof item === "string") return item;
|
|
256
|
+
const { type, data } = item;
|
|
257
|
+
if (type === "text") return data.text;
|
|
258
|
+
return data.text ? `{${type}}(${data.text})` : `{${type}}`;
|
|
259
|
+
})
|
|
260
|
+
.join("");
|
|
261
|
+
}
|
|
262
|
+
export function toString(content: SendContent) {
|
|
263
|
+
if (!Array.isArray(content)) content = [content];
|
|
264
|
+
return content
|
|
265
|
+
.map((item) => {
|
|
266
|
+
if (typeof item === "string") return item;
|
|
267
|
+
let { type, data } = item;
|
|
268
|
+
if (typeof type === "function") type = type.name;
|
|
269
|
+
if (type === "text") return data.text;
|
|
270
|
+
return `<${type} ${Object.keys(data)
|
|
271
|
+
.map((key) => `${key}='${escape(JSON.stringify(data[key]))}'`)
|
|
272
|
+
.join(" ")}/>`;
|
|
273
|
+
})
|
|
274
|
+
.join("");
|
|
275
|
+
}
|
|
194
276
|
}
|
|
195
277
|
|
|
196
278
|
export function remove<T>(list: T[], fn: (item: T) => boolean): void;
|
|
197
279
|
export function remove<T>(list: T[], item: T): void;
|
|
198
280
|
export function remove<T>(list: T[], arg: T | ((item: T) => boolean)) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
281
|
+
const index =
|
|
282
|
+
typeof arg === "function" &&
|
|
283
|
+
!list.every((item) => typeof item === "function")
|
|
284
|
+
? list.findIndex(arg as (item: T) => boolean)
|
|
285
|
+
: list.indexOf(arg as T);
|
|
286
|
+
if (index !== -1) list.splice(index, 1);
|
|
204
287
|
}
|
|
205
|
-
export function isEmpty<T>(item:T){
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
288
|
+
export function isEmpty<T>(item: T) {
|
|
289
|
+
if (Array.isArray(item)) return item.length === 0;
|
|
290
|
+
if (typeof item === "object") {
|
|
291
|
+
if (!item) return true;
|
|
292
|
+
return Reflect.ownKeys(item).length === 0;
|
|
293
|
+
}
|
|
294
|
+
return false;
|
|
212
295
|
}
|
|
213
296
|
|
|
214
297
|
export namespace Time {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
298
|
+
export const millisecond = 1;
|
|
299
|
+
export const second = 1000;
|
|
300
|
+
export const minute = second * 60;
|
|
301
|
+
export const hour = minute * 60;
|
|
302
|
+
export const day = hour * 24;
|
|
303
|
+
export const week = day * 7;
|
|
221
304
|
|
|
222
|
-
|
|
305
|
+
let timezoneOffset = new Date().getTimezoneOffset();
|
|
223
306
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
307
|
+
export function setTimezoneOffset(offset: number) {
|
|
308
|
+
timezoneOffset = offset;
|
|
309
|
+
}
|
|
227
310
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
311
|
+
export function getTimezoneOffset() {
|
|
312
|
+
return timezoneOffset;
|
|
313
|
+
}
|
|
231
314
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
315
|
+
export function getDateNumber(
|
|
316
|
+
date: number | Date = new Date(),
|
|
317
|
+
offset?: number
|
|
318
|
+
) {
|
|
319
|
+
if (typeof date === "number") date = new Date(date);
|
|
320
|
+
if (offset === undefined) offset = timezoneOffset;
|
|
321
|
+
return Math.floor((date.valueOf() / minute - offset) / 1440);
|
|
322
|
+
}
|
|
237
323
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
324
|
+
export function fromDateNumber(value: number, offset?: number) {
|
|
325
|
+
const date = new Date(value * day);
|
|
326
|
+
if (offset === undefined) offset = timezoneOffset;
|
|
327
|
+
return new Date(+date + offset * minute);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const numeric = /\d+(?:\.\d+)?/.source;
|
|
331
|
+
const timeRegExp = new RegExp(
|
|
332
|
+
`^${[
|
|
333
|
+
"w(?:eek(?:s)?)?",
|
|
334
|
+
"d(?:ay(?:s)?)?",
|
|
335
|
+
"h(?:our(?:s)?)?",
|
|
336
|
+
"m(?:in(?:ute)?(?:s)?)?",
|
|
337
|
+
"s(?:ec(?:ond)?(?:s)?)?",
|
|
338
|
+
]
|
|
339
|
+
.map((unit) => `(${numeric}${unit})?`)
|
|
340
|
+
.join("")}$`
|
|
341
|
+
);
|
|
243
342
|
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
343
|
+
export function parseTime(source: string) {
|
|
344
|
+
const capture = timeRegExp.exec(source);
|
|
345
|
+
if (!capture) return 0;
|
|
346
|
+
return (
|
|
347
|
+
(parseFloat(capture[1]) * week || 0) +
|
|
348
|
+
(parseFloat(capture[2]) * day || 0) +
|
|
349
|
+
(parseFloat(capture[3]) * hour || 0) +
|
|
350
|
+
(parseFloat(capture[4]) * minute || 0) +
|
|
351
|
+
(parseFloat(capture[5]) * second || 0)
|
|
249
352
|
);
|
|
353
|
+
}
|
|
250
354
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
(parseFloat(capture[5]) * second || 0)
|
|
260
|
-
);
|
|
355
|
+
export function parseDate(date: string) {
|
|
356
|
+
const parsed = parseTime(date);
|
|
357
|
+
if (parsed) {
|
|
358
|
+
date = (Date.now() + parsed) as any;
|
|
359
|
+
} else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
|
|
360
|
+
date = `${new Date().toLocaleDateString()}-${date}`;
|
|
361
|
+
} else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
|
|
362
|
+
date = `${new Date().getFullYear()}-${date}`;
|
|
261
363
|
}
|
|
364
|
+
return date ? new Date(date) : new Date();
|
|
365
|
+
}
|
|
262
366
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
367
|
+
export function formatTimeShort(ms: number) {
|
|
368
|
+
const abs = Math.abs(ms);
|
|
369
|
+
if (abs >= day - hour / 2) {
|
|
370
|
+
return Math.round(ms / day) + "d";
|
|
371
|
+
} else if (abs >= hour - minute / 2) {
|
|
372
|
+
return Math.round(ms / hour) + "h";
|
|
373
|
+
} else if (abs >= minute - second / 2) {
|
|
374
|
+
return Math.round(ms / minute) + "m";
|
|
375
|
+
} else if (abs >= second) {
|
|
376
|
+
return Math.round(ms / second) + "s";
|
|
273
377
|
}
|
|
378
|
+
return ms + "ms";
|
|
379
|
+
}
|
|
274
380
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
381
|
+
export function formatTime(ms: number) {
|
|
382
|
+
let result: string;
|
|
383
|
+
if (ms >= day - hour / 2) {
|
|
384
|
+
ms += hour / 2;
|
|
385
|
+
result = Math.floor(ms / day) + " 天";
|
|
386
|
+
if (ms % day > hour) {
|
|
387
|
+
result += ` ${Math.floor((ms % day) / hour)} 小时`;
|
|
388
|
+
}
|
|
389
|
+
} else if (ms >= hour - minute / 2) {
|
|
390
|
+
ms += minute / 2;
|
|
391
|
+
result = Math.floor(ms / hour) + " 小时";
|
|
392
|
+
if (ms % hour > minute) {
|
|
393
|
+
result += ` ${Math.floor((ms % hour) / minute)} 分钟`;
|
|
394
|
+
}
|
|
395
|
+
} else if (ms >= minute - second / 2) {
|
|
396
|
+
ms += second / 2;
|
|
397
|
+
result = Math.floor(ms / minute) + " 分钟";
|
|
398
|
+
if (ms % minute > second) {
|
|
399
|
+
result += ` ${Math.floor((ms % minute) / second)} 秒`;
|
|
400
|
+
}
|
|
401
|
+
} else {
|
|
402
|
+
result = Math.round(ms / second) + " 秒";
|
|
287
403
|
}
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
288
406
|
|
|
289
|
-
|
|
290
|
-
let result: string;
|
|
291
|
-
if (ms >= day - hour / 2) {
|
|
292
|
-
ms += hour / 2;
|
|
293
|
-
result = Math.floor(ms / day) + ' 天';
|
|
294
|
-
if (ms % day > hour) {
|
|
295
|
-
result += ` ${Math.floor((ms % day) / hour)} 小时`;
|
|
296
|
-
}
|
|
297
|
-
} else if (ms >= hour - minute / 2) {
|
|
298
|
-
ms += minute / 2;
|
|
299
|
-
result = Math.floor(ms / hour) + ' 小时';
|
|
300
|
-
if (ms % hour > minute) {
|
|
301
|
-
result += ` ${Math.floor((ms % hour) / minute)} 分钟`;
|
|
302
|
-
}
|
|
303
|
-
} else if (ms >= minute - second / 2) {
|
|
304
|
-
ms += second / 2;
|
|
305
|
-
result = Math.floor(ms / minute) + ' 分钟';
|
|
306
|
-
if (ms % minute > second) {
|
|
307
|
-
result += ` ${Math.floor((ms % minute) / second)} 秒`;
|
|
308
|
-
}
|
|
309
|
-
} else {
|
|
310
|
-
result = Math.round(ms / second) + ' 秒';
|
|
311
|
-
}
|
|
312
|
-
return result;
|
|
313
|
-
}
|
|
407
|
+
const dayMap = ["日", "一", "二", "三", "四", "五", "六"];
|
|
314
408
|
|
|
315
|
-
|
|
409
|
+
function toDigits(source: number, length = 2) {
|
|
410
|
+
return source.toString().padStart(length, "0");
|
|
411
|
+
}
|
|
316
412
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
413
|
+
export function template(template: string, time = new Date()) {
|
|
414
|
+
return template
|
|
415
|
+
.replace("yyyy", time.getFullYear().toString())
|
|
416
|
+
.replace("yy", time.getFullYear().toString().slice(2))
|
|
417
|
+
.replace("MM", toDigits(time.getMonth() + 1))
|
|
418
|
+
.replace("dd", toDigits(time.getDate()))
|
|
419
|
+
.replace("hh", toDigits(time.getHours()))
|
|
420
|
+
.replace("mm", toDigits(time.getMinutes()))
|
|
421
|
+
.replace("ss", toDigits(time.getSeconds()))
|
|
422
|
+
.replace("SSS", toDigits(time.getMilliseconds(), 3));
|
|
423
|
+
}
|
|
320
424
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
.replace('yy', time.getFullYear().toString().slice(2))
|
|
325
|
-
.replace('MM', toDigits(time.getMonth() + 1))
|
|
326
|
-
.replace('dd', toDigits(time.getDate()))
|
|
327
|
-
.replace('hh', toDigits(time.getHours()))
|
|
328
|
-
.replace('mm', toDigits(time.getMinutes()))
|
|
329
|
-
.replace('ss', toDigits(time.getSeconds()))
|
|
330
|
-
.replace('SSS', toDigits(time.getMilliseconds(), 3));
|
|
331
|
-
}
|
|
425
|
+
function toHourMinute(time: Date) {
|
|
426
|
+
return `${toDigits(time.getHours())}:${toDigits(time.getMinutes())}`;
|
|
427
|
+
}
|
|
332
428
|
|
|
333
|
-
|
|
334
|
-
|
|
429
|
+
export function formatTimeInterval(time: Date, interval?: number) {
|
|
430
|
+
if (!interval) {
|
|
431
|
+
return template("yyyy-MM-dd hh:mm:ss", time);
|
|
432
|
+
} else if (interval === day) {
|
|
433
|
+
return `每天 ${toHourMinute(time)}`;
|
|
434
|
+
} else if (interval === week) {
|
|
435
|
+
return `每周${dayMap[time.getDay()]} ${toHourMinute(time)}`;
|
|
436
|
+
} else {
|
|
437
|
+
return `${template("yyyy-MM-dd hh:mm:ss", time)} 起每隔 ${formatTime(
|
|
438
|
+
interval
|
|
439
|
+
)}`;
|
|
335
440
|
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
export const supportedPluginExtensions = [
|
|
444
|
+
".js",
|
|
445
|
+
".ts",
|
|
446
|
+
".mjs",
|
|
447
|
+
".cjs",
|
|
448
|
+
".jsx",
|
|
449
|
+
".tsx",
|
|
450
|
+
"",
|
|
451
|
+
];
|
|
336
452
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
453
|
+
export function resolveEntry(entry: string) {
|
|
454
|
+
if (fs.existsSync(entry)) {
|
|
455
|
+
const stat = fs.statSync(entry);
|
|
456
|
+
if (stat.isFile()) return entry;
|
|
457
|
+
if (stat.isSymbolicLink()) return resolveEntry(fs.realpathSync(entry));
|
|
458
|
+
if (stat.isDirectory()) {
|
|
459
|
+
const packageJsonPath = path.resolve(entry, 'package.json');
|
|
460
|
+
if (!fs.existsSync(packageJsonPath)) return resolveEntry(path.join(entry, 'index'));
|
|
461
|
+
const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
462
|
+
return resolveEntry(path.resolve(entry, pkgJson.main || 'index.js'));
|
|
347
463
|
}
|
|
464
|
+
} else {
|
|
465
|
+
for (const ext of supportedPluginExtensions) {
|
|
466
|
+
const fullPath = path.resolve(entry + ext);
|
|
467
|
+
if (fs.existsSync(fullPath)) return resolveEntry(fullPath);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
348
470
|
}
|
|
349
471
|
export function sleep(ms: number) {
|
|
350
|
-
|
|
351
|
-
}
|
|
472
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
473
|
+
}
|