befly 1.0.3 → 1.1.0
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/README.md +1 -1
- package/main.js +133 -38
- package/package.json +2 -2
package/README.md
CHANGED
package/main.js
CHANGED
|
@@ -10,7 +10,7 @@ import { Crypto2 } from './utils/crypto.js';
|
|
|
10
10
|
import { XMLParser } from './libs/xml/XMLParser.js';
|
|
11
11
|
import { isEmptyObject, isType, pickFields, sortPlugins, RYes, RNo, filename2, dirname2 } from './utils/util.js';
|
|
12
12
|
|
|
13
|
-
class
|
|
13
|
+
class Befly {
|
|
14
14
|
constructor(options = {}) {
|
|
15
15
|
this.apiRoutes = new Map();
|
|
16
16
|
this.pluginLists = [];
|
|
@@ -20,6 +20,9 @@ class BunPii {
|
|
|
20
20
|
|
|
21
21
|
async initCheck() {
|
|
22
22
|
try {
|
|
23
|
+
const checkStartTime = Bun.nanoseconds();
|
|
24
|
+
Logger.info('开始执行系统检查...');
|
|
25
|
+
|
|
23
26
|
const checksDir = path.join(dirname2(import.meta.url), 'checks');
|
|
24
27
|
const glob = new Bun.Glob('*.js');
|
|
25
28
|
|
|
@@ -39,6 +42,7 @@ class BunPii {
|
|
|
39
42
|
|
|
40
43
|
try {
|
|
41
44
|
totalChecks++;
|
|
45
|
+
const singleCheckStart = Bun.nanoseconds();
|
|
42
46
|
|
|
43
47
|
// 导入检查模块
|
|
44
48
|
const check = await import(file);
|
|
@@ -46,19 +50,24 @@ class BunPii {
|
|
|
46
50
|
// 执行默认导出的函数
|
|
47
51
|
if (typeof check.default === 'function') {
|
|
48
52
|
const checkResult = await check.default(this.appContext);
|
|
53
|
+
const singleCheckTime = (Bun.nanoseconds() - singleCheckStart) / 1_000_000;
|
|
54
|
+
|
|
49
55
|
if (checkResult === true) {
|
|
50
56
|
passedChecks++;
|
|
57
|
+
Logger.info(`检查 ${fileName} 通过,耗时: ${singleCheckTime.toFixed(2)}ms`);
|
|
51
58
|
} else {
|
|
52
|
-
Logger.error(`检查未通过: ${fileName}`);
|
|
59
|
+
Logger.error(`检查未通过: ${fileName},耗时: ${singleCheckTime.toFixed(2)}ms`);
|
|
53
60
|
failedChecks++;
|
|
54
61
|
}
|
|
55
62
|
} else {
|
|
56
|
-
|
|
63
|
+
const singleCheckTime = (Bun.nanoseconds() - singleCheckStart) / 1_000_000;
|
|
64
|
+
Logger.warn(`文件 ${fileName} 未导出默认函数,耗时: ${singleCheckTime.toFixed(2)}ms`);
|
|
57
65
|
failedChecks++;
|
|
58
66
|
}
|
|
59
67
|
} catch (error) {
|
|
68
|
+
const singleCheckTime = (Bun.nanoseconds() - singleCheckStart) / 1_000_000;
|
|
60
69
|
Logger.error({
|
|
61
|
-
msg: `检查失败 ${fileName}`,
|
|
70
|
+
msg: `检查失败 ${fileName},耗时: ${singleCheckTime.toFixed(2)}ms`,
|
|
62
71
|
error: error.message,
|
|
63
72
|
stack: error.stack
|
|
64
73
|
});
|
|
@@ -66,8 +75,10 @@ class BunPii {
|
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
77
|
|
|
78
|
+
const totalCheckTime = (Bun.nanoseconds() - checkStartTime) / 1_000_000;
|
|
79
|
+
|
|
69
80
|
// 输出检查结果统计
|
|
70
|
-
Logger.info(
|
|
81
|
+
Logger.info(`系统检查完成! 总耗时: ${totalCheckTime.toFixed(2)}ms,总检查数: ${totalChecks}, 通过: ${passedChecks}, 失败: ${failedChecks}`);
|
|
71
82
|
|
|
72
83
|
if (failedChecks > 0) {
|
|
73
84
|
process.exit();
|
|
@@ -78,7 +89,7 @@ class BunPii {
|
|
|
78
89
|
}
|
|
79
90
|
} catch (error) {
|
|
80
91
|
Logger.error({
|
|
81
|
-
msg: '
|
|
92
|
+
msg: '执行系统检查时发生错误',
|
|
82
93
|
error: error.message,
|
|
83
94
|
stack: error.stack
|
|
84
95
|
});
|
|
@@ -88,12 +99,17 @@ class BunPii {
|
|
|
88
99
|
|
|
89
100
|
async loadPlugins() {
|
|
90
101
|
try {
|
|
102
|
+
const loadStartTime = Bun.nanoseconds();
|
|
103
|
+
Logger.info('开始加载插件...');
|
|
104
|
+
|
|
91
105
|
const glob = new Bun.Glob('*.js');
|
|
92
106
|
const corePlugins = [];
|
|
93
107
|
const userPlugins = [];
|
|
94
108
|
const loadedPluginNames = new Set(); // 用于跟踪已加载的插件名称
|
|
95
109
|
|
|
96
|
-
//
|
|
110
|
+
// 扫描核心插件目录
|
|
111
|
+
Logger.info('正在扫描核心插件...');
|
|
112
|
+
const corePluginsScanStart = Bun.nanoseconds();
|
|
97
113
|
for await (const file of glob.scan({
|
|
98
114
|
cwd: path.join(dirname2(import.meta.url), 'plugins'),
|
|
99
115
|
onlyFiles: true,
|
|
@@ -101,12 +117,20 @@ class BunPii {
|
|
|
101
117
|
})) {
|
|
102
118
|
const fileName = path.basename(file, '.js');
|
|
103
119
|
if (fileName.startsWith('_')) continue;
|
|
120
|
+
|
|
121
|
+
const importStart = Bun.nanoseconds();
|
|
104
122
|
const plugin = await import(file);
|
|
123
|
+
const importTime = (Bun.nanoseconds() - importStart) / 1_000_000; // 转换为毫秒
|
|
124
|
+
|
|
105
125
|
const pluginInstance = plugin.default;
|
|
106
126
|
pluginInstance.pluginName = fileName;
|
|
107
127
|
corePlugins.push(pluginInstance);
|
|
108
128
|
loadedPluginNames.add(fileName); // 记录已加载的核心插件名称
|
|
129
|
+
|
|
130
|
+
Logger.info(`核心插件 ${fileName} 导入耗时: ${importTime.toFixed(2)}ms`);
|
|
109
131
|
}
|
|
132
|
+
const corePluginsScanTime = (Bun.nanoseconds() - corePluginsScanStart) / 1_000_000;
|
|
133
|
+
Logger.info(`核心插件扫描完成,耗时: ${corePluginsScanTime.toFixed(2)}ms,共找到 ${corePlugins.length} 个插件`);
|
|
110
134
|
|
|
111
135
|
const sortedCorePlugins = sortPlugins(corePlugins);
|
|
112
136
|
if (sortedCorePlugins === false) {
|
|
@@ -114,16 +138,26 @@ class BunPii {
|
|
|
114
138
|
process.exit();
|
|
115
139
|
}
|
|
116
140
|
|
|
141
|
+
// 初始化核心插件
|
|
142
|
+
Logger.info('正在初始化核心插件...');
|
|
143
|
+
const corePluginsInitStart = Bun.nanoseconds();
|
|
117
144
|
for (const plugin of sortedCorePlugins) {
|
|
118
145
|
try {
|
|
146
|
+
const initStart = Bun.nanoseconds();
|
|
119
147
|
this.pluginLists.push(plugin);
|
|
120
148
|
this.appContext[plugin.pluginName] = typeof plugin?.onInit === 'function' ? await plugin?.onInit(this.appContext) : {};
|
|
149
|
+
const initTime = (Bun.nanoseconds() - initStart) / 1_000_000;
|
|
150
|
+
Logger.info(`核心插件 ${plugin.pluginName} 初始化耗时: ${initTime.toFixed(2)}ms`);
|
|
121
151
|
} catch (error) {
|
|
122
152
|
Logger.warn(`插件 ${plugin.pluginName} 初始化失败:`, error.message);
|
|
123
153
|
}
|
|
124
154
|
}
|
|
155
|
+
const corePluginsInitTime = (Bun.nanoseconds() - corePluginsInitStart) / 1_000_000;
|
|
156
|
+
Logger.info(`核心插件初始化完成,耗时: ${corePluginsInitTime.toFixed(2)}ms`);
|
|
125
157
|
|
|
126
|
-
//
|
|
158
|
+
// 扫描用户插件目录
|
|
159
|
+
Logger.info('正在扫描用户插件...');
|
|
160
|
+
const userPluginsScanStart = Bun.nanoseconds();
|
|
127
161
|
for await (const file of glob.scan({
|
|
128
162
|
cwd: path.join(process.cwd(), 'plugins'),
|
|
129
163
|
onlyFiles: true,
|
|
@@ -138,11 +172,18 @@ class BunPii {
|
|
|
138
172
|
continue;
|
|
139
173
|
}
|
|
140
174
|
|
|
175
|
+
const importStart = Bun.nanoseconds();
|
|
141
176
|
const plugin = await import(file);
|
|
177
|
+
const importTime = (Bun.nanoseconds() - importStart) / 1_000_000; // 转换为毫秒
|
|
178
|
+
|
|
142
179
|
const pluginInstance = plugin.default;
|
|
143
180
|
pluginInstance.pluginName = fileName;
|
|
144
181
|
userPlugins.push(pluginInstance);
|
|
182
|
+
|
|
183
|
+
Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime.toFixed(2)}ms`);
|
|
145
184
|
}
|
|
185
|
+
const userPluginsScanTime = (Bun.nanoseconds() - userPluginsScanStart) / 1_000_000;
|
|
186
|
+
Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime.toFixed(2)}ms,共找到 ${userPlugins.length} 个插件`);
|
|
146
187
|
|
|
147
188
|
const sortedUserPlugins = sortPlugins(userPlugins);
|
|
148
189
|
if (sortedUserPlugins === false) {
|
|
@@ -150,14 +191,28 @@ class BunPii {
|
|
|
150
191
|
process.exit();
|
|
151
192
|
}
|
|
152
193
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
194
|
+
// 初始化用户插件
|
|
195
|
+
if (userPlugins.length > 0) {
|
|
196
|
+
Logger.info('正在初始化用户插件...');
|
|
197
|
+
const userPluginsInitStart = Bun.nanoseconds();
|
|
198
|
+
for (const plugin of sortedUserPlugins) {
|
|
199
|
+
try {
|
|
200
|
+
const initStart = Bun.nanoseconds();
|
|
201
|
+
this.pluginLists.push(plugin);
|
|
202
|
+
this.appContext[plugin.pluginName] = typeof plugin?.onInit === 'function' ? await plugin?.onInit(this.appContext) : {};
|
|
203
|
+
const initTime = (Bun.nanoseconds() - initStart) / 1_000_000;
|
|
204
|
+
Logger.info(`用户插件 ${plugin.pluginName} 初始化耗时: ${initTime.toFixed(2)}ms`);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
Logger.warn(`插件 ${plugin.pluginName} 初始化失败:`, error.message);
|
|
207
|
+
}
|
|
159
208
|
}
|
|
209
|
+
const userPluginsInitTime = (Bun.nanoseconds() - userPluginsInitStart) / 1_000_000;
|
|
210
|
+
Logger.info(`用户插件初始化完成,耗时: ${userPluginsInitTime.toFixed(2)}ms`);
|
|
160
211
|
}
|
|
212
|
+
|
|
213
|
+
const totalLoadTime = (Bun.nanoseconds() - loadStartTime) / 1_000_000;
|
|
214
|
+
const totalPluginCount = sortedCorePlugins.length + sortedUserPlugins.length;
|
|
215
|
+
Logger.info(`插件加载完成! 总耗时: ${totalLoadTime.toFixed(2)}ms,共加载 ${totalPluginCount} 个插件`);
|
|
161
216
|
} catch (error) {
|
|
162
217
|
Logger.error({
|
|
163
218
|
msg: '加载插件时发生错误',
|
|
@@ -168,10 +223,19 @@ class BunPii {
|
|
|
168
223
|
}
|
|
169
224
|
async loadApis(dirName) {
|
|
170
225
|
try {
|
|
226
|
+
const loadStartTime = Bun.nanoseconds();
|
|
227
|
+
const dirDisplayName = dirName === 'core' ? '核心' : '用户';
|
|
228
|
+
Logger.info(`开始加载${dirDisplayName}接口...`);
|
|
229
|
+
|
|
171
230
|
const coreApisDir = path.join(dirname2(import.meta.url), 'apis');
|
|
172
231
|
const userApisDir = path.join(process.cwd(), 'apis');
|
|
173
232
|
const glob = new Bun.Glob('**/*.js');
|
|
174
233
|
const apiDir = dirName === 'core' ? coreApisDir : userApisDir;
|
|
234
|
+
|
|
235
|
+
let totalApis = 0;
|
|
236
|
+
let loadedApis = 0;
|
|
237
|
+
let failedApis = 0;
|
|
238
|
+
|
|
175
239
|
// 扫描指定目录
|
|
176
240
|
for await (const file of glob.scan({
|
|
177
241
|
cwd: apiDir,
|
|
@@ -181,29 +245,50 @@ class BunPii {
|
|
|
181
245
|
const fileName = path.basename(file, '.js');
|
|
182
246
|
const apiPath = path.relative(apiDir, file).replace(/\.js$/, '').replace(/\\/g, '/');
|
|
183
247
|
if (apiPath.indexOf('_') !== -1) continue;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
248
|
+
|
|
249
|
+
totalApis++;
|
|
250
|
+
const singleApiStart = Bun.nanoseconds();
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const api = (await import(file)).default;
|
|
254
|
+
if (isType(api.name, 'string') === false || api.name.trim() === '') {
|
|
255
|
+
throw new Error(`接口 ${apiPath} 的 name 属性必须是非空字符串`);
|
|
256
|
+
}
|
|
257
|
+
if (api.auth !== false && api.auth !== true && Array.isArray(api.auth) === false) {
|
|
258
|
+
throw new Error(`接口 ${apiPath} 的 auth 属性必须是布尔值或字符串数组`);
|
|
259
|
+
}
|
|
260
|
+
if (isType(api.fields, 'object') === false) {
|
|
261
|
+
throw new Error(`接口 ${apiPath} 的 fields 属性必须是对象`);
|
|
262
|
+
}
|
|
263
|
+
if (isType(api.required, 'array') === false) {
|
|
264
|
+
throw new Error(`接口 ${apiPath} 的 required 属性必须是数组`);
|
|
265
|
+
}
|
|
266
|
+
// 数组的每一项都必须是字符串
|
|
267
|
+
if (api.required.some((item) => isType(item, 'string') === false)) {
|
|
268
|
+
throw new Error(`接口 ${apiPath} 的 required 属性必须是字符串数组`);
|
|
269
|
+
}
|
|
270
|
+
if (isType(api.handler, 'function') === false) {
|
|
271
|
+
throw new Error(`接口 ${apiPath} 的 handler 属性必须是函数`);
|
|
272
|
+
}
|
|
273
|
+
api.route = `${api.method.toUpperCase()}/api/${dirName}/${apiPath}`;
|
|
274
|
+
this.apiRoutes.set(api.route, api);
|
|
275
|
+
|
|
276
|
+
const singleApiTime = (Bun.nanoseconds() - singleApiStart) / 1_000_000;
|
|
277
|
+
loadedApis++;
|
|
278
|
+
// Logger.info(`${dirDisplayName}接口 ${apiPath} 加载成功,耗时: ${singleApiTime.toFixed(2)}ms`);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
const singleApiTime = (Bun.nanoseconds() - singleApiStart) / 1_000_000;
|
|
281
|
+
failedApis++;
|
|
282
|
+
Logger.error({
|
|
283
|
+
msg: `${dirDisplayName}接口 ${apiPath} 加载失败,耗时: ${singleApiTime.toFixed(2)}ms`,
|
|
284
|
+
error: error.message,
|
|
285
|
+
stack: error.stack
|
|
286
|
+
});
|
|
203
287
|
}
|
|
204
|
-
api.route = `${api.method.toUpperCase()}/api/${dirName}/${apiPath}`;
|
|
205
|
-
this.apiRoutes.set(api.route, api);
|
|
206
288
|
}
|
|
289
|
+
|
|
290
|
+
const totalLoadTime = (Bun.nanoseconds() - loadStartTime) / 1_000_000;
|
|
291
|
+
Logger.info(`${dirDisplayName}接口加载完成! 总耗时: ${totalLoadTime.toFixed(2)}ms,总数: ${totalApis}, 成功: ${loadedApis}, 失败: ${failedApis}`);
|
|
207
292
|
} catch (error) {
|
|
208
293
|
Logger.error({
|
|
209
294
|
msg: '加载接口时发生错误',
|
|
@@ -217,11 +302,17 @@ class BunPii {
|
|
|
217
302
|
* 启动服务器
|
|
218
303
|
*/
|
|
219
304
|
async listen(callback) {
|
|
305
|
+
const serverStartTime = Bun.nanoseconds();
|
|
306
|
+
Logger.info('开始启动 Befly 服务器...');
|
|
307
|
+
|
|
220
308
|
await this.initCheck();
|
|
221
309
|
await this.loadPlugins();
|
|
222
310
|
await this.loadApis('core');
|
|
223
311
|
await this.loadApis('app');
|
|
224
312
|
|
|
313
|
+
const totalStartupTime = (Bun.nanoseconds() - serverStartTime) / 1_000_000;
|
|
314
|
+
Logger.info(`服务器启动准备完成,总耗时: ${totalStartupTime.toFixed(2)}ms`);
|
|
315
|
+
|
|
225
316
|
const server = Bun.serve({
|
|
226
317
|
port: Env.APP_PORT,
|
|
227
318
|
hostname: Env.APP_HOST,
|
|
@@ -229,7 +320,7 @@ class BunPii {
|
|
|
229
320
|
'/': async (req) => {
|
|
230
321
|
return Response.json({
|
|
231
322
|
code: 0,
|
|
232
|
-
msg: '
|
|
323
|
+
msg: 'Befly 接口服务已启动',
|
|
233
324
|
data: {
|
|
234
325
|
mode: Env.NODE_ENV
|
|
235
326
|
}
|
|
@@ -327,7 +418,7 @@ class BunPii {
|
|
|
327
418
|
}
|
|
328
419
|
|
|
329
420
|
// 请求记录
|
|
330
|
-
Logger.
|
|
421
|
+
Logger.info({
|
|
331
422
|
msg: '通用接口日志',
|
|
332
423
|
请求路径: apiPath,
|
|
333
424
|
请求方法: req.method,
|
|
@@ -400,10 +491,14 @@ class BunPii {
|
|
|
400
491
|
}
|
|
401
492
|
});
|
|
402
493
|
|
|
494
|
+
const finalStartupTime = (Bun.nanoseconds() - serverStartTime) / 1_000_000;
|
|
495
|
+
Logger.info(`🚀 Befly 服务器启动成功! 完整启动耗时: ${finalStartupTime.toFixed(2)}ms`);
|
|
496
|
+
Logger.info(`📡 服务器监听地址: http://${Env.APP_HOST}:${Env.APP_PORT}`);
|
|
497
|
+
|
|
403
498
|
if (callback && typeof callback === 'function') {
|
|
404
499
|
callback(server);
|
|
405
500
|
}
|
|
406
501
|
}
|
|
407
502
|
}
|
|
408
503
|
|
|
409
|
-
export {
|
|
504
|
+
export { Befly, Env, Api, Jwt, Crypto2, Logger, RYes, RNo };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"README.md",
|
|
50
50
|
"vitest.config.js"
|
|
51
51
|
],
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "c86df1d32fd5321912fed97a1bf7c1348ce9c730"
|
|
53
53
|
}
|