befly 3.8.19 → 3.8.20

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 (49) hide show
  1. package/README.md +7 -6
  2. package/bunfig.toml +1 -1
  3. package/lib/database.ts +28 -25
  4. package/lib/dbHelper.ts +3 -3
  5. package/lib/jwt.ts +90 -99
  6. package/lib/logger.ts +44 -23
  7. package/lib/redisHelper.ts +19 -22
  8. package/loader/loadApis.ts +69 -114
  9. package/loader/loadHooks.ts +65 -0
  10. package/loader/loadPlugins.ts +50 -219
  11. package/main.ts +106 -133
  12. package/package.json +15 -7
  13. package/paths.ts +20 -0
  14. package/plugins/cache.ts +1 -3
  15. package/plugins/db.ts +8 -11
  16. package/plugins/logger.ts +5 -3
  17. package/plugins/redis.ts +10 -14
  18. package/router/api.ts +60 -106
  19. package/router/root.ts +15 -12
  20. package/router/static.ts +54 -58
  21. package/sync/syncAll.ts +58 -0
  22. package/sync/syncApi.ts +264 -0
  23. package/sync/syncDb/apply.ts +194 -0
  24. package/sync/syncDb/constants.ts +76 -0
  25. package/sync/syncDb/ddl.ts +194 -0
  26. package/sync/syncDb/helpers.ts +200 -0
  27. package/sync/syncDb/index.ts +164 -0
  28. package/sync/syncDb/schema.ts +201 -0
  29. package/sync/syncDb/sqlite.ts +50 -0
  30. package/sync/syncDb/table.ts +321 -0
  31. package/sync/syncDb/tableCreate.ts +146 -0
  32. package/sync/syncDb/version.ts +72 -0
  33. package/sync/syncDb.ts +19 -0
  34. package/sync/syncDev.ts +206 -0
  35. package/sync/syncMenu.ts +331 -0
  36. package/tsconfig.json +2 -4
  37. package/types/api.d.ts +6 -0
  38. package/types/befly.d.ts +152 -28
  39. package/types/context.d.ts +29 -3
  40. package/types/hook.d.ts +35 -0
  41. package/types/index.ts +14 -1
  42. package/types/plugin.d.ts +6 -7
  43. package/types/sync.d.ts +403 -0
  44. package/check.ts +0 -378
  45. package/env.ts +0 -106
  46. package/lib/middleware.ts +0 -275
  47. package/types/env.ts +0 -65
  48. package/types/util.d.ts +0 -45
  49. package/util.ts +0 -257
@@ -0,0 +1,331 @@
1
+ /**
2
+ * SyncMenu 命令 - 同步菜单数据到数据库
3
+ * 说明:根据 menu.json 配置文件增量同步菜单数据(最多3级:父级、子级、孙级)
4
+ *
5
+ * 流程:
6
+ * 1. 扫描项目根目录和所有 addon 的 menu.json 配置文件
7
+ * 2. 项目的 menu.json 优先级最高,可以覆盖 addon 的菜单配置
8
+ * 3. 文件不存在或格式错误时默认为空数组
9
+ * 4. 根据菜单的 path 字段检查是否存在
10
+ * 5. 存在则更新其他字段(name、sort、type、pid)
11
+ * 6. 不存在则新增菜单记录
12
+ * 7. 强制删除配置中不存在的菜单记录
13
+ * 注:state 字段由框架自动管理(1=正常,2=禁用,0=删除)
14
+ */
15
+
16
+ import { join } from 'pathe';
17
+ import { existsSync } from 'node:fs';
18
+ import { Database } from '../lib/database.js';
19
+ import { RedisHelper } from '../lib/redisHelper.js';
20
+ import { scanAddons, getAddonDir } from 'befly-util';
21
+ import { Logger } from '../lib/logger.js';
22
+ import { projectDir } from '../paths.js';
23
+
24
+ import type { SyncMenuOptions, MenuConfig, BeflyOptions } from '../types/index.js';
25
+
26
+ /**
27
+ * 读取菜单配置文件
28
+ * 如果文件不存在或不是数组格式,返回空数组
29
+ */
30
+ async function readMenuConfig(filePath: string): Promise<MenuConfig[]> {
31
+ try {
32
+ if (!existsSync(filePath)) {
33
+ return [];
34
+ }
35
+
36
+ const content = await import(filePath, { with: { type: 'json' } });
37
+
38
+ // 验证是否为数组
39
+ if (!Array.isArray(content.default)) {
40
+ return [];
41
+ }
42
+
43
+ return content.default;
44
+ } catch (error: any) {
45
+ Logger.warn(`读取菜单配置失败 ${filePath}: ${error.message}`);
46
+ return [];
47
+ }
48
+ }
49
+
50
+ /**
51
+ * 为 addon 菜单的 path 添加前缀
52
+ * 规则:
53
+ * 1. 所有路径必须以 / 开头
54
+ * 2. 所有路径都添加 /addon/{addonName} 前缀(包括根路径 /)
55
+ * 3. 递归处理所有层级的子菜单
56
+ * 4. 项目菜单不添加前缀
57
+ */
58
+ function addAddonPrefix(menus: MenuConfig[], addonName: string): MenuConfig[] {
59
+ return menus.map((menu) => {
60
+ const newMenu = { ...menu };
61
+
62
+ // 处理当前菜单的 path(包括根路径 /)
63
+ if (newMenu.path && newMenu.path.startsWith('/')) {
64
+ newMenu.path = `/addon/${addonName}${newMenu.path}`;
65
+ }
66
+
67
+ // 递归处理子菜单
68
+ if (newMenu.children && newMenu.children.length > 0) {
69
+ newMenu.children = addAddonPrefix(newMenu.children, addonName);
70
+ }
71
+
72
+ return newMenu;
73
+ });
74
+ }
75
+
76
+ /**
77
+ * 合并菜单配置
78
+ * 优先级:项目 menu.json > addon menu.json
79
+ * 支持三级菜单结构:父级、子级、孙级
80
+ */
81
+ function mergeMenuConfigs(allMenus: Array<{ menus: MenuConfig[]; addonName: string }>): MenuConfig[] {
82
+ /**
83
+ * 递归合并指定层级的菜单(限制最多3层)
84
+ * @param menus 待合并的菜单数组
85
+ * @param depth 当前深度(1=父级, 2=子级, 3=孙级)
86
+ * @returns 合并后的菜单数组
87
+ */
88
+ function mergeLevel(menus: MenuConfig[], depth: number = 1): MenuConfig[] {
89
+ const menuMap = new Map<string, MenuConfig>();
90
+
91
+ for (const menu of menus) {
92
+ if (!menu.path) continue;
93
+
94
+ const existing = menuMap.get(menu.path);
95
+ if (existing) {
96
+ // 合并子菜单
97
+ if (menu.children?.length > 0) {
98
+ existing.children = existing.children || [];
99
+ existing.children.push(...menu.children);
100
+ }
101
+ } else {
102
+ menuMap.set(menu.path, { ...menu });
103
+ }
104
+ }
105
+
106
+ // 递归处理子菜单(限制最多3层)
107
+ if (depth < 3) {
108
+ for (const menu of menuMap.values()) {
109
+ if (menu.children?.length > 0) {
110
+ menu.children = mergeLevel(menu.children, depth + 1);
111
+ }
112
+ }
113
+ }
114
+
115
+ return Array.from(menuMap.values());
116
+ }
117
+
118
+ // 收集所有菜单(扁平化)
119
+ const allFlatMenus = allMenus.flatMap(({ menus }) => menus);
120
+
121
+ return mergeLevel(allFlatMenus, 1);
122
+ }
123
+
124
+ /**
125
+ * 收集配置文件中所有菜单的 path(最多3级)
126
+ * 子级菜单使用独立路径
127
+ */
128
+ function collectPaths(menus: MenuConfig[]): Set<string> {
129
+ const paths = new Set<string>();
130
+
131
+ for (const menu of menus) {
132
+ if (menu.path) {
133
+ paths.add(menu.path);
134
+ }
135
+ if (menu.children && menu.children.length > 0) {
136
+ for (const child of menu.children) {
137
+ if (child.path) {
138
+ paths.add(child.path);
139
+ }
140
+ // 第三层菜单
141
+ if (child.children && child.children.length > 0) {
142
+ for (const grandChild of child.children) {
143
+ if (grandChild.path) {
144
+ paths.add(grandChild.path);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ return paths;
153
+ }
154
+
155
+ /**
156
+ * 递归同步单个菜单(限制最多3层)
157
+ * @param helper 数据库帮助类
158
+ * @param menu 菜单配置
159
+ * @param pid 父级菜单ID
160
+ * @param existingMenuMap 现有菜单映射
161
+ * @param depth 当前深度(1=父级, 2=子级, 3=孙级)
162
+ * @returns 菜单ID
163
+ */
164
+ async function syncMenuRecursive(helper: any, menu: MenuConfig, pid: number, existingMenuMap: Map<string, any>, depth: number = 1): Promise<number> {
165
+ const existing = existingMenuMap.get(menu.path || '');
166
+ let menuId: number;
167
+
168
+ if (existing) {
169
+ menuId = existing.id;
170
+
171
+ // 检查是否需要更新
172
+ const needUpdate = existing.pid !== pid || existing.name !== menu.name || existing.sort !== (menu.sort || 0);
173
+
174
+ if (needUpdate) {
175
+ await helper.updData({
176
+ table: 'addon_admin_menu',
177
+ where: { id: existing.id },
178
+ data: {
179
+ pid: pid,
180
+ name: menu.name,
181
+ sort: menu.sort || 0
182
+ }
183
+ });
184
+ }
185
+ } else {
186
+ menuId = await helper.insData({
187
+ table: 'addon_admin_menu',
188
+ data: {
189
+ pid: pid,
190
+ name: menu.name,
191
+ path: menu.path || '',
192
+ sort: menu.sort || 0
193
+ }
194
+ });
195
+ }
196
+
197
+ // 递归处理子菜单(限制最多3层)
198
+ if (depth < 3 && menu.children?.length > 0) {
199
+ for (const child of menu.children) {
200
+ await syncMenuRecursive(helper, child, menuId, existingMenuMap, depth + 1);
201
+ }
202
+ }
203
+
204
+ return menuId;
205
+ }
206
+
207
+ /**
208
+ * 同步菜单(三层结构:父级、子级、孙级)
209
+ * 子级菜单使用独立路径
210
+ */
211
+ async function syncMenus(helper: any, menus: MenuConfig[]): Promise<void> {
212
+ // 批量查询所有现有菜单,建立 path -> menu 的映射
213
+ const allExistingMenus = await helper.getAll({
214
+ table: 'addon_admin_menu',
215
+ fields: ['id', 'pid', 'name', 'path', 'sort']
216
+ });
217
+ const existingMenuMap = new Map<string, any>();
218
+ for (const menu of allExistingMenus) {
219
+ if (menu.path) {
220
+ existingMenuMap.set(menu.path, menu);
221
+ }
222
+ }
223
+
224
+ for (const menu of menus) {
225
+ try {
226
+ await syncMenuRecursive(helper, menu, 0, existingMenuMap, 1);
227
+ } catch (error: any) {
228
+ Logger.error(`同步菜单 "${menu.name}" 失败`, error.message || String(error));
229
+ throw error;
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * 删除配置中不存在的菜单(强制删除)
236
+ */
237
+ async function deleteObsoleteRecords(helper: any, configPaths: Set<string>): Promise<void> {
238
+ const allRecords = await helper.getAll({
239
+ table: 'addon_admin_menu',
240
+ fields: ['id', 'path'],
241
+ where: { state$gte: 0 }
242
+ });
243
+
244
+ for (const record of allRecords) {
245
+ if (record.path && !configPaths.has(record.path)) {
246
+ await helper.delForce({
247
+ table: 'addon_admin_menu',
248
+ where: { id: record.id }
249
+ });
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * SyncMenu 命令主函数
256
+ */
257
+ export async function syncMenuCommand(config: BeflyOptions, options: SyncMenuOptions = {}): Promise<void> {
258
+ try {
259
+ if (options.plan) {
260
+ Logger.debug('[计划] 同步菜单配置到数据库(plan 模式不执行)');
261
+ return;
262
+ }
263
+
264
+ // 1. 扫描所有 addon 的 menu.json 配置文件
265
+ const allMenus: Array<{ menus: MenuConfig[]; addonName: string }> = [];
266
+
267
+ const addonNames = scanAddons();
268
+
269
+ for (const addonName of addonNames) {
270
+ const addonMenuPath = getAddonDir(addonName, 'menu.json');
271
+ if (existsSync(addonMenuPath)) {
272
+ const addonMenus = await readMenuConfig(addonMenuPath);
273
+ if (addonMenus.length > 0) {
274
+ // 为 addon 菜单添加路径前缀
275
+ const menusWithPrefix = addAddonPrefix(addonMenus, addonName);
276
+ allMenus.push({ menus: menusWithPrefix, addonName: addonName });
277
+ }
278
+ }
279
+ }
280
+
281
+ // 2. 读取项目根目录的 menu.json(优先级最高,不添加前缀)
282
+ const projectMenuPath = join(projectDir, 'menu.json');
283
+ const projectMenus = await readMenuConfig(projectMenuPath);
284
+ if (projectMenus.length > 0) {
285
+ allMenus.push({ menus: projectMenus, addonName: 'project' });
286
+ } // 3. 合并菜单配置(项目配置优先)
287
+ const mergedMenus = mergeMenuConfigs(allMenus);
288
+
289
+ // 连接数据库(SQL + Redis)
290
+ await Database.connect();
291
+
292
+ const helper = Database.getDbHelper();
293
+
294
+ // 4. 检查表是否存在(addon_admin_menu 来自 addon-admin 组件)
295
+ const exists = await helper.tableExists('addon_admin_menu');
296
+
297
+ if (!exists) {
298
+ Logger.debug('表 addon_admin_menu 不存在,跳过菜单同步(需要安装 addon-admin 组件)');
299
+ return;
300
+ }
301
+
302
+ // 5. 收集配置文件中所有菜单的 path
303
+ const configPaths = collectPaths(mergedMenus);
304
+
305
+ // 6. 同步菜单
306
+ await syncMenus(helper, mergedMenus);
307
+
308
+ // 7. 删除文件中不存在的菜单(强制删除)
309
+ await deleteObsoleteRecords(helper, configPaths);
310
+
311
+ // 8. 获取最终菜单数据(用于缓存)
312
+ const allMenusData = await helper.getAll({
313
+ table: 'addon_admin_menu',
314
+ fields: ['id', 'pid', 'name', 'path', 'sort'],
315
+ orderBy: ['sort#ASC', 'id#ASC']
316
+ });
317
+
318
+ // 9. 缓存菜单数据到 Redis
319
+ try {
320
+ const redisHelper = new RedisHelper();
321
+ await redisHelper.setObject('menus:all', allMenusData);
322
+ } catch (error: any) {
323
+ Logger.warn(`Redis 缓存菜单数据失败: ${error.message}`);
324
+ }
325
+ } catch (error: any) {
326
+ Logger.error('菜单同步失败', error);
327
+ throw error;
328
+ } finally {
329
+ await Database?.disconnect();
330
+ }
331
+ }
package/tsconfig.json CHANGED
@@ -33,9 +33,7 @@
33
33
  "isolatedModules": true,
34
34
 
35
35
  // 输出
36
- "declaration": true,
37
- "declarationMap": true,
38
- "sourceMap": true,
36
+ "noEmit": true,
39
37
  "removeComments": false,
40
38
  "importHelpers": false,
41
39
 
@@ -45,7 +43,7 @@
45
43
  "checkJs": false,
46
44
 
47
45
  // Bun 特定
48
- "types": ["bun-types"],
46
+ "types": ["bun"],
49
47
 
50
48
  // 路径
51
49
  "baseUrl": ".",
package/types/api.d.ts CHANGED
@@ -84,6 +84,12 @@ export interface ApiRoute<T = any, R = any> {
84
84
  /** 必填字段(可选,默认 []) */
85
85
  required?: string[];
86
86
 
87
+ /** 缓存配置(可选,单位:秒) */
88
+ cache?: number;
89
+
90
+ /** 限流配置(可选,格式:次数/秒,如 "10/60" 表示 60秒内10次) */
91
+ rateLimit?: string;
92
+
87
93
  /** 路由路径(运行时生成) */
88
94
  route?: string;
89
95
  }
package/types/befly.d.ts CHANGED
@@ -12,46 +12,170 @@ import type { Database } from 'bun:sqlite';
12
12
  import type { DbHelper } from '../lib/dbHelper.js';
13
13
  import type { RedisHelper } from '../lib/redisHelper.js';
14
14
  import type { Cipher } from '../lib/cipher.js';
15
+ import type { CacheHelper } from '../lib/cacheHelper.js';
15
16
 
16
17
  /**
17
- * Befly 应用选项
18
+ * 日志配置
18
19
  */
19
- export interface BeflyOptions {
20
- /** 应用名称 */
21
- name?: string;
20
+ export interface LoggerConfig {
21
+ /** 是否开启调试模式 (0: 关闭, 1: 开启) @default 1 */
22
+ debug?: number;
23
+ /** 日志排除字段 (逗号分隔) @default 'password,token,secret' */
24
+ excludeFields?: string;
25
+ /** 日志目录 @default './logs' */
26
+ dir?: string;
27
+ /** 是否输出到控制台 (0: 关闭, 1: 开启) @default 1 */
28
+ console?: number;
29
+ /** 单个日志文件最大大小 (字节) @default 10485760 (10MB) */
30
+ maxSize?: number;
31
+ }
22
32
 
23
- /** 监听主机 */
33
+ /**
34
+ * 数据库配置
35
+ */
36
+ export interface DatabaseConfig {
37
+ /** 是否启用数据库 (0: 关闭, 1: 开启) @default 0 */
38
+ enable?: number;
39
+ /** 数据库类型 ('mysql' | 'postgres' | 'sqlite') @default 'sqlite' */
40
+ type?: string;
41
+ /** 数据库主机 @default '127.0.0.1' */
24
42
  host?: string;
25
-
26
- /** 监听端口 */
43
+ /** 数据库端口 @default 3306 */
27
44
  port?: number;
45
+ /** 数据库用户名 @default 'root' */
46
+ username?: string;
47
+ /** 数据库密码 @default '' */
48
+ password?: string;
49
+ /** 数据库名称 @default 'befly' */
50
+ database?: string;
51
+ /** 连接池最大连接数 @default 1 */
52
+ poolMax?: number;
53
+ }
28
54
 
29
- /** 是否启用 CORS */
30
- cors?: boolean | CorsOptions;
31
-
32
- /** 静态文件目录 */
33
- staticDir?: string;
55
+ /**
56
+ * Redis 配置
57
+ */
58
+ export interface RedisConfig {
59
+ /** Redis 主机 @default '127.0.0.1' */
60
+ host?: string;
61
+ /** Redis 端口 @default 6379 */
62
+ port?: number;
63
+ /** Redis 用户名 @default '' */
64
+ username?: string;
65
+ /** Redis 密码 @default '' */
66
+ password?: string;
67
+ /** Redis 数据库索引 @default 0 */
68
+ db?: number;
69
+ /** Redis Key 前缀 @default 'befly:' */
70
+ prefix?: string;
71
+ }
34
72
 
35
- /** 上传文件目录 */
36
- uploadDir?: string;
73
+ /**
74
+ * JWT 配置
75
+ */
76
+ export interface AuthConfig {
77
+ /** JWT 密钥 @default 'befly-secret' */
78
+ secret?: string;
79
+ /** 过期时间 @default '7d' */
80
+ expiresIn?: string | number;
81
+ /** 签名算法 @default 'HS256' */
82
+ algorithm?: string;
83
+ }
37
84
 
38
- /** 最大请求体大小 */
39
- maxBodySize?: number;
85
+ /**
86
+ * CORS 配置
87
+ */
88
+ export interface CorsConfig {
89
+ /** 允许的来源 @default '*' */
90
+ origin?: string;
91
+ /** 允许的方法 @default 'GET,HEAD,PUT,PATCH,POST,DELETE' */
92
+ methods?: string;
93
+ /** 允许的头 @default 'Content-Type,Authorization' */
94
+ allowedHeaders?: string;
95
+ /** 暴露的头 @default '' */
96
+ exposedHeaders?: string;
97
+ /** 预检请求缓存时间 (秒) @default 86400 */
98
+ maxAge?: number;
99
+ /** 是否允许凭证 @default 'true' */
100
+ credentials?: string;
101
+ }
40
102
 
41
- /** 自定义配置 */
103
+ /**
104
+ * Befly 构造函数选项(最多 2 级结构)
105
+ */
106
+ export interface BeflyOptions {
107
+ // ========== 核心参数 ==========
108
+ /** 运行环境 ('development' | 'production') @default 'development' */
109
+ nodeEnv?: string;
110
+ /** 应用名称 @default 'Befly App' */
111
+ appName?: string;
112
+ /** 应用端口 @default 3000 */
113
+ appPort?: number;
114
+ /** 应用主机 @default '0.0.0.0' */
115
+ appHost?: string;
116
+ /** 开发者邮箱 (用于 syncDev) @default 'dev@qq.com' */
117
+ devEmail?: string;
118
+ /** 开发者密码 (用于 syncDev) */
119
+ devPassword?: string;
120
+ /** 请求体大小限制 (字节) @default 1048576 (1MB) */
121
+ bodyLimit?: number;
122
+ /** 时区 @default 'Asia/Shanghai' */
123
+ tz?: string;
124
+
125
+ // ========== 插件配置 ==========
126
+ /** 日志插件配置 */
127
+ logger?: LoggerConfig;
128
+ /** 数据库插件配置 */
129
+ db?: DatabaseConfig;
130
+ /** Redis 插件配置 */
131
+ redis?: RedisConfig;
132
+ /** 认证插件配置 */
133
+ auth?: AuthConfig;
134
+ /** CORS 插件配置 */
135
+ cors?: CorsConfig;
136
+ /** 禁用的钩子列表 */
137
+ disableHooks?: string[];
138
+ /** 禁用的插件列表 */
139
+ disablePlugins?: string[];
140
+ /** 其他插件配置 */
42
141
  [key: string]: any;
43
142
  }
44
143
 
45
144
  /**
46
- * CORS 配置选项
47
- */
48
- export interface CorsOptions {
49
- origin?: string | string[] | boolean;
50
- methods?: string[];
51
- allowedHeaders?: string[];
52
- exposedHeaders?: string[];
53
- credentials?: boolean;
54
- maxAge?: number;
145
+ * Befly 应用上下文
146
+ * 包含所有插件挂载的实例
147
+ */
148
+ export interface BeflyContext extends KeyValue {
149
+ // ========== 核心插件(带下划线前缀) ==========
150
+ /** 数据库助手 (db 插件) */
151
+ _db?: DbHelper;
152
+
153
+ /** Redis 助手 (redis 插件) */
154
+ _redis?: RedisHelper;
155
+
156
+ /** 日志器 (logger 插件) */
157
+ _logger?: typeof Logger;
158
+
159
+ /** 缓存助手 (cache 插件) */
160
+ _cache?: CacheHelper;
161
+
162
+ // ========== 核心插件便捷访问(无前缀) ==========
163
+ /** 数据库助手便捷访问 */
164
+ db?: DbHelper;
165
+
166
+ /** Redis 助手便捷访问 */
167
+ redis?: RedisHelper;
168
+
169
+ /** 日志器便捷访问 */
170
+ logger?: typeof Logger;
171
+
172
+ /** 缓存助手便捷访问 */
173
+ cache?: CacheHelper;
174
+
175
+ // ========== 动态插件 ==========
176
+ /** 组件插件:addon_{addonName}_{pluginName} */
177
+ /** 项目插件:app_{pluginName} */
178
+ [key: string]: any;
55
179
  }
56
180
 
57
181
  /**
@@ -122,9 +246,9 @@ export interface Befly {
122
246
  handleRequest(request: Request): Promise<Response>;
123
247
 
124
248
  /**
125
- * 注册中间件
249
+ * 注册钩子
126
250
  */
127
- use(middleware: Function): void;
251
+ use(hook: Function): void;
128
252
 
129
253
  /**
130
254
  * 获取配置
@@ -1,7 +1,33 @@
1
1
  /**
2
2
  * 请求上下文类型定义
3
- * 实现代码位于 util.ts,类型定义位于 types/util.d.ts
4
3
  */
5
4
 
6
- // 重新导出 RequestContext 接口
7
- export type { RequestContext } from './util.js';
5
+ import type { ApiRoute } from './api.js';
6
+
7
+ /**
8
+ * 请求上下文接口
9
+ */
10
+ export interface RequestContext {
11
+ /** 请求体参数 */
12
+ body: Record<string, any>;
13
+ /** 用户信息 */
14
+ user: Record<string, any>;
15
+ /** 原始请求对象 */
16
+ req: Request;
17
+ /** 请求开始时间(毫秒) */
18
+ now: number;
19
+ /** 客户端 IP 地址 */
20
+ ip?: string;
21
+ /** API 路由路径(如 POST/api/user/login) */
22
+ route?: string;
23
+ /** 当前请求的 API 路由对象 */
24
+ api?: ApiRoute;
25
+ /** 响应对象(如果设置了此属性,将直接返回该响应) */
26
+ response?: Response;
27
+ /** 原始处理结果(未转换为 Response 对象前) */
28
+ result?: any;
29
+ /** 请求唯一 ID */
30
+ requestId?: string;
31
+ /** CORS 响应头 */
32
+ corsHeaders?: Record<string, string>;
33
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Befly 钩子系统类型定义
3
+ */
4
+
5
+ import type { BeflyContext } from './befly.js';
6
+ import type { RequestContext } from './context.js';
7
+
8
+ /**
9
+ * 钩子处理函数类型
10
+ */
11
+ export type Next = () => Promise<void>;
12
+ export type HookHandler = (befly: BeflyContext, ctx: RequestContext, next: Next) => Promise<void> | void;
13
+
14
+ /**
15
+ * 钩子配置类型
16
+ */
17
+ export interface Hook {
18
+ /** 钩子名称(运行时动态添加,由文件名生成) */
19
+ name?: string;
20
+
21
+ /** 依赖的钩子列表(在这些钩子之后执行) */
22
+ after?: string[];
23
+
24
+ /** 执行顺序(数字越小越先执行,同级别时使用) */
25
+ order?: number;
26
+
27
+ /** 钩子处理函数 */
28
+ handler: HookHandler;
29
+
30
+ /** 钩子配置 */
31
+ config?: Record<string, any>;
32
+
33
+ /** 钩子描述 */
34
+ description?: string;
35
+ }
package/types/index.ts CHANGED
@@ -2,4 +2,17 @@
2
2
  * 类型定义导出
3
3
  */
4
4
 
5
- export * from './env.js';
5
+ export * from './addon.js';
6
+ export * from './api.js';
7
+ export * from './befly.js';
8
+ export * from './common.js';
9
+ export * from './context.js';
10
+ export * from './crypto.js';
11
+ export * from './database.js';
12
+ export * from './jwt.js';
13
+ export * from './logger.js';
14
+ export * from './plugin.js';
15
+ export * from './redis.js';
16
+ export * from './tool.js';
17
+ export * from './validator.js';
18
+ export * from './sync.js';
package/types/plugin.d.ts CHANGED
@@ -13,16 +13,15 @@ export type PluginInitFunction = (befly: BeflyContext) => Promise<any> | any;
13
13
  /**
14
14
  * 插件请求处理钩子函数类型
15
15
  */
16
- export type PluginGetHook = (befly: BeflyContext, ctx: RequestContext, req: Request) => Promise<void> | void;
16
+ export type Next = () => Promise<void>;
17
17
 
18
18
  /**
19
19
  * 插件配置类型
20
20
  */
21
21
  export interface Plugin {
22
- /** 插件名称 */
23
- name: string;
24
-
25
- /** 插件名称(运行时动态添加,与 name 相同) */
22
+ /** 插件名称(运行时动态添加,由文件名生成) */
23
+ name?: string;
24
+ /** @deprecated use name instead */
26
25
  pluginName?: string;
27
26
 
28
27
  /** 依赖的插件列表(在这些插件之后执行) */
@@ -31,8 +30,8 @@ export interface Plugin {
31
30
  /** 插件初始化函数 */
32
31
  onInit?: PluginInitFunction;
33
32
 
34
- /** 请求处理钩子(在每个 API 请求时执行) */
35
- onGet?: PluginGetHook;
33
+ /** 插件配置 */
34
+ config?: Record<string, any>;
36
35
 
37
36
  /** 插件描述 */
38
37
  description?: string;