@zhin.js/core 1.0.17 → 1.0.19

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.
Files changed (112) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/REFACTORING_COMPLETE.md +178 -0
  3. package/REFACTORING_STATUS.md +263 -0
  4. package/lib/adapter.d.ts +44 -19
  5. package/lib/adapter.d.ts.map +1 -1
  6. package/lib/adapter.js +81 -50
  7. package/lib/adapter.js.map +1 -1
  8. package/lib/bot.d.ts +7 -12
  9. package/lib/bot.d.ts.map +1 -1
  10. package/lib/built/adapter-process.d.ts +36 -0
  11. package/lib/built/adapter-process.d.ts.map +1 -0
  12. package/lib/built/adapter-process.js +77 -0
  13. package/lib/built/adapter-process.js.map +1 -0
  14. package/lib/built/command.d.ts +46 -0
  15. package/lib/built/command.d.ts.map +1 -0
  16. package/lib/built/command.js +54 -0
  17. package/lib/built/command.js.map +1 -0
  18. package/lib/built/component.d.ts +42 -0
  19. package/lib/built/component.d.ts.map +1 -0
  20. package/lib/built/component.js +66 -0
  21. package/lib/built/component.js.map +1 -0
  22. package/lib/built/config.d.ts +31 -0
  23. package/lib/built/config.d.ts.map +1 -0
  24. package/lib/built/config.js +141 -0
  25. package/lib/built/config.js.map +1 -0
  26. package/lib/built/cron.d.ts +53 -0
  27. package/lib/built/cron.d.ts.map +1 -0
  28. package/lib/built/cron.js +79 -0
  29. package/lib/built/cron.js.map +1 -0
  30. package/lib/built/database.d.ts +17 -0
  31. package/lib/built/database.d.ts.map +1 -0
  32. package/lib/built/database.js +28 -0
  33. package/lib/built/database.js.map +1 -0
  34. package/lib/{permissions.d.ts → built/permission.d.ts} +5 -10
  35. package/lib/built/permission.d.ts.map +1 -0
  36. package/lib/{permissions.js → built/permission.js} +11 -10
  37. package/lib/built/permission.js.map +1 -0
  38. package/lib/command.d.ts +7 -7
  39. package/lib/command.d.ts.map +1 -1
  40. package/lib/command.js +5 -15
  41. package/lib/command.js.map +1 -1
  42. package/lib/component.d.ts.map +1 -1
  43. package/lib/component.js.map +1 -1
  44. package/lib/cron.d.ts +1 -0
  45. package/lib/cron.d.ts.map +1 -1
  46. package/lib/cron.js +2 -0
  47. package/lib/cron.js.map +1 -1
  48. package/lib/index.d.ts +11 -3
  49. package/lib/index.d.ts.map +1 -1
  50. package/lib/index.js +14 -4
  51. package/lib/index.js.map +1 -1
  52. package/lib/jsx-runtime.d.ts +2 -2
  53. package/lib/message.d.ts +2 -2
  54. package/lib/message.d.ts.map +1 -1
  55. package/lib/plugin.d.ts +164 -51
  56. package/lib/plugin.d.ts.map +1 -1
  57. package/lib/plugin.js +521 -150
  58. package/lib/plugin.js.map +1 -1
  59. package/lib/prompt.d.ts +1 -1
  60. package/lib/prompt.d.ts.map +1 -1
  61. package/lib/prompt.js +2 -1
  62. package/lib/prompt.js.map +1 -1
  63. package/lib/types.d.ts +33 -33
  64. package/lib/types.d.ts.map +1 -1
  65. package/lib/utils.d.ts +16 -1
  66. package/lib/utils.d.ts.map +1 -1
  67. package/lib/utils.js +166 -66
  68. package/lib/utils.js.map +1 -1
  69. package/package.json +19 -9
  70. package/src/adapter.ts +131 -80
  71. package/src/bot.ts +8 -13
  72. package/src/built/adapter-process.ts +77 -0
  73. package/src/built/command.ts +102 -0
  74. package/src/built/component.ts +111 -0
  75. package/src/built/config.ts +126 -0
  76. package/src/built/cron.ts +140 -0
  77. package/src/built/database.ts +38 -0
  78. package/src/{permissions.ts → built/permission.ts} +9 -12
  79. package/src/command.ts +11 -20
  80. package/src/component.ts +0 -1
  81. package/src/cron.ts +2 -0
  82. package/src/index.ts +15 -5
  83. package/src/message.ts +2 -2
  84. package/src/plugin.ts +671 -202
  85. package/src/prompt.ts +4 -3
  86. package/src/types.ts +41 -35
  87. package/src/utils.ts +418 -296
  88. package/test/minimal-bot.ts +31 -0
  89. package/test/stress-test.ts +123 -0
  90. package/tests/command.test.ts +47 -44
  91. package/ASYNC-JSX-SUPPORT.md +0 -173
  92. package/lib/app.d.ts +0 -191
  93. package/lib/app.d.ts.map +0 -1
  94. package/lib/app.js +0 -604
  95. package/lib/app.js.map +0 -1
  96. package/lib/config.d.ts +0 -54
  97. package/lib/config.d.ts.map +0 -1
  98. package/lib/config.js +0 -308
  99. package/lib/config.js.map +0 -1
  100. package/lib/log-transport.d.ts +0 -37
  101. package/lib/log-transport.d.ts.map +0 -1
  102. package/lib/log-transport.js +0 -136
  103. package/lib/log-transport.js.map +0 -1
  104. package/lib/permissions.d.ts.map +0 -1
  105. package/lib/permissions.js.map +0 -1
  106. package/src/app.ts +0 -772
  107. package/src/config.ts +0 -397
  108. package/src/log-transport.ts +0 -163
  109. package/tests/app.test.ts +0 -265
  110. package/tests/permissions.test.ts +0 -358
  111. package/tests/plugin.test.ts +0 -234
  112. package/tests/prompt.test.ts +0 -223
package/src/utils.ts CHANGED
@@ -1,351 +1,473 @@
1
- import {AdapterMessage, Dict, MessageElement, MessageMiddleware, RegisteredAdapter, SendContent} from "./types";
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
- const result = evaluate(template, ctx);
6
- if (result === `return(${template})`) return undefined;
7
- return result;
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
- const result = execute<S, T>(`return(${exp})`, context);
11
- // 如果结果是原始表达式,说明访问被阻止,返回 undefined
12
- if (result === `return(${exp})`) return undefined;
13
- return result;
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>(middlewares: MessageMiddleware<P>[]){
21
- return function(message: Message<AdapterMessage<P>>, next: () => Promise<void>=()=>Promise.resolve()){
22
- let index = -1
23
- const dispatch=async (i:number=0):Promise<void>=>{
24
- if(i<=index) return Promise.reject(new Error('next() called multiple times'));
25
- index=i
26
- let fn=middlewares[i]
27
- if(i===middlewares.length) fn=next
28
- if(!fn) return;
29
- try {
30
- return await fn(message, ()=>dispatch(i+1));
31
- } catch (error) {
32
- // 中间件异常应该被记录但不中断整个流程
33
- console.error('Middleware error:', error);
34
- throw error;
35
- }
36
- };
37
- return dispatch(0);
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
- export const execute = <S, T = any>(exp: string, context: S):T => {
42
- const fn = evalCache[exp] || (evalCache[exp] = toFunction(exp));
43
- context={
44
- ...context,
45
- process:{
46
- version:process.version,
47
- versions:process.versions,
48
- platform:process.platform,
49
- arch:process.arch,
50
- release:process.release,
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
- try {
71
- return new Function(`$data`, `with($data){${exp}}`);
72
- } catch {
73
- return () => {};
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
- const matched = [...template.matchAll(/\${([^}]*?)}/g)];
78
- for (const item of matched) {
79
- const tpl = item[1];
80
- let value = getValueWithRuntime(tpl, ctx);
81
- if (value === tpl) continue;
82
- if (typeof value !== 'string') value = JSON.stringify(value, null, 2);
83
- template = template.replace(`\${${item[1]}}`, value);
84
- }
85
- return template;
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
- return {
89
- type,
90
- data
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
- export function escape<T>(text: T): T {
95
- if (typeof text !== 'string') return text;
96
- return text
97
- .replace(/&/g, '&amp;')
98
- .replace(/</g, '&lt;')
99
- .replace(/>/g, '&gt;')
100
- .replace(/"/g, '&quot;')
101
- .replace(/'/g, '&#39;') as T;
102
- }
103
- export function unescape<T>(text: T): T {
104
- if (typeof text !== 'string') return text;
105
- return text
106
- .replace(/&lt;/g, '<')
107
- .replace(/&gt;/g, '>')
108
- .replace(/&quot;/g, '"')
109
- .replace(/&#39;/g, "'")
110
- .replace(/&amp;/g, '&') as T;
111
- }
112
- export function text(text:string){
113
- return segment('text',{text});
114
- }
115
- export function face(id:string,text?:string){
116
- return segment('face',{id,text});
117
- }
118
- export function from(content: SendContent): SendContent {
119
- if (!Array.isArray(content)) content=[content];
120
- const toString=(template:string|MessageElement)=>{
121
- if(typeof template!=='string') return [template]
122
- template=unescape(template);
123
- const result: MessageElement[] = [];
124
- const closingReg = /<(\S+)(\s[^>]+)?\/>/;
125
- const twinningReg = /<(\S+)(\s[^>]+)?>([\s\S]*?)<\/\1>/;
126
- while (template.length) {
127
- const [_, type, attrStr = '', child = ''] = template.match(twinningReg) || template.match(closingReg) || [];
128
- if (!type) break;
129
- const isClosing = closingReg.test(template);
130
- const matched = isClosing ? `<${type}${attrStr}/>` : `<${type}${attrStr}>${child}</${type}>`;
131
- const index = template.indexOf(matched);
132
- const prevText = template.slice(0, index);
133
- if (prevText)
134
- result.push({
135
- type: 'text',
136
- data: {
137
- text: unescape(prevText),
138
- },
139
- });
140
- template = template.slice(index + matched.length);
141
- const attrArr = [...attrStr.matchAll(/\s([^=]+)(?=(?=="([^"]+)")|(?=='([^']+)'))/g)];
142
- const data = Object.fromEntries(
143
- attrArr.map(([source, key, v1, v2]) => {
144
- const value = v1 || v2;
145
- try {
146
- return [key, JSON.parse(unescape(value))];
147
- } catch {
148
- return [key, unescape(value)];
149
- }
150
- }),
151
- );
152
- if (child) {
153
- data.message = toString(child).map(({ type, data }) => ({ type, ...data }));
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, "&amp;")
166
+ .replace(/</g, "&lt;")
167
+ .replace(/>/g, "&gt;")
168
+ .replace(/"/g, "&quot;")
169
+ .replace(/'/g, "&#39;") as T;
170
+ }
171
+ export function unescape<T>(text: T): T {
172
+ if (typeof text !== "string") return text;
173
+ return text
174
+ .replace(/&lt;/g, "<")
175
+ .replace(/&gt;/g, ">")
176
+ .replace(/&quot;/g, '"')
177
+ .replace(/&#39;/g, "'")
178
+ .replace(/&amp;/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
- return result;
223
+ })
224
+ );
225
+ if (child) {
226
+ data.message = toString(child).map(({ type, data }) => ({
227
+ type,
228
+ ...data,
229
+ }));
169
230
  }
170
- return content.reduce((result,item)=>{
171
- result.push(...toString(item))
172
- return result;
173
- },[] as MessageElement[])
174
- }
175
- export function raw(content:SendContent){
176
- if(!Array.isArray(content)) content=[content]
177
- return content.map(item=>{
178
- if(typeof item==='string') return item
179
- const {type,data}=item
180
- if(type==='text') return data.text
181
- return data.text?`{${type}}(${data.text})`:`{${type}}`;
182
- }).join('')
183
- }
184
- export function toString(content:SendContent){
185
- if(!Array.isArray(content)) content=[content]
186
- return content.map(item=>{
187
- if(typeof item==='string') return item
188
- let {type,data}=item
189
- if(typeof type==='function') type=type.name
190
- if(type==='text') return data.text
191
- return `<${type} ${Object.keys(data).map(key=>`${key}='${escape(JSON.stringify(data[key]))}'`).join(' ')}/>`
192
- }).join('')
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
- const index =
200
- typeof arg === 'function' && !list.every(item => typeof item === 'function')
201
- ? list.findIndex(arg as (item: T) => boolean)
202
- : list.indexOf(arg as T);
203
- if (index !== -1) list.splice(index, 1);
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
- if(Array.isArray(item)) return item.length===0
207
- if(typeof item==='object'){
208
- if(!item) return true
209
- return Reflect.ownKeys(item).length===0
210
- }
211
- return false
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
- export const millisecond = 1;
216
- export const second = 1000;
217
- export const minute = second * 60;
218
- export const hour = minute * 60;
219
- export const day = hour * 24;
220
- export const week = day * 7;
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
- let timezoneOffset = new Date().getTimezoneOffset();
305
+ let timezoneOffset = new Date().getTimezoneOffset();
223
306
 
224
- export function setTimezoneOffset(offset: number) {
225
- timezoneOffset = offset;
226
- }
307
+ export function setTimezoneOffset(offset: number) {
308
+ timezoneOffset = offset;
309
+ }
227
310
 
228
- export function getTimezoneOffset() {
229
- return timezoneOffset;
230
- }
311
+ export function getTimezoneOffset() {
312
+ return timezoneOffset;
313
+ }
231
314
 
232
- export function getDateNumber(date: number | Date = new Date(), offset?: number) {
233
- if (typeof date === 'number') date = new Date(date);
234
- if (offset === undefined) offset = timezoneOffset;
235
- return Math.floor((date.valueOf() / minute - offset) / 1440);
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
- export function fromDateNumber(value: number, offset?: number) {
239
- const date = new Date(value * day);
240
- if (offset === undefined) offset = timezoneOffset;
241
- return new Date(+date + offset * minute);
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
- const numeric = /\d+(?:\.\d+)?/.source;
245
- const timeRegExp = new RegExp(
246
- `^${['w(?:eek(?:s)?)?', 'd(?:ay(?:s)?)?', 'h(?:our(?:s)?)?', 'm(?:in(?:ute)?(?:s)?)?', 's(?:ec(?:ond)?(?:s)?)?']
247
- .map(unit => `(${numeric}${unit})?`)
248
- .join('')}$`,
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
- export function parseTime(source: string) {
252
- const capture = timeRegExp.exec(source);
253
- if (!capture) return 0;
254
- return (
255
- (parseFloat(capture[1]) * week || 0) +
256
- (parseFloat(capture[2]) * day || 0) +
257
- (parseFloat(capture[3]) * hour || 0) +
258
- (parseFloat(capture[4]) * minute || 0) +
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
- export function parseDate(date: string) {
264
- const parsed = parseTime(date);
265
- if (parsed) {
266
- date = (Date.now() + parsed) as any;
267
- } else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
268
- date = `${new Date().toLocaleDateString()}-${date}`;
269
- } else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) {
270
- date = `${new Date().getFullYear()}-${date}`;
271
- }
272
- return date ? new Date(date) : new Date();
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
- export function formatTimeShort(ms: number) {
276
- const abs = Math.abs(ms);
277
- if (abs >= day - hour / 2) {
278
- return Math.round(ms / day) + 'd';
279
- } else if (abs >= hour - minute / 2) {
280
- return Math.round(ms / hour) + 'h';
281
- } else if (abs >= minute - second / 2) {
282
- return Math.round(ms / minute) + 'm';
283
- } else if (abs >= second) {
284
- return Math.round(ms / second) + 's';
285
- }
286
- return ms + 'ms';
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
- export function formatTime(ms: number) {
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
- const dayMap = ['日', '一', '二', '三', '四', '五', '六'];
409
+ function toDigits(source: number, length = 2) {
410
+ return source.toString().padStart(length, "0");
411
+ }
316
412
 
317
- function toDigits(source: number, length = 2) {
318
- return source.toString().padStart(length, '0');
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
- export function template(template: string, time = new Date()) {
322
- return template
323
- .replace('yyyy', time.getFullYear().toString())
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
- function toHourMinute(time: Date) {
334
- return `${toDigits(time.getHours())}:${toDigits(time.getMinutes())}`;
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
- export function formatTimeInterval(time: Date, interval?: number) {
338
- if (!interval) {
339
- return template('yyyy-MM-dd hh:mm:ss', time);
340
- } else if (interval === day) {
341
- return `每天 ${toHourMinute(time)}`;
342
- } else if (interval === week) {
343
- return `每周${dayMap[time.getDay()]} ${toHourMinute(time)}`;
344
- } else {
345
- return `${template('yyyy-MM-dd hh:mm:ss', time)} 起每隔 ${formatTime(interval)}`;
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
- return new Promise(resolve => setTimeout(resolve, ms));
351
- }
472
+ return new Promise((resolve) => setTimeout(resolve, ms));
473
+ }