befly 3.8.8 → 3.8.10

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.
@@ -0,0 +1,2 @@
1
+ LICENSE
2
+ LICENSE.md
package/.prettierrc ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "trailingComma": "none",
3
+ "tabWidth": 4,
4
+ "semi": true,
5
+ "singleQuote": true,
6
+ "printWidth": 1024,
7
+ "bracketSpacing": true,
8
+ "useTabs": false,
9
+ "arrowParens": "always",
10
+ "endOfLine": "lf",
11
+ "plugins": ["@prettier/plugin-oxc"]
12
+ }
package/bunfig.toml ADDED
@@ -0,0 +1,3 @@
1
+ [install]
2
+ # 使用隔离模式
3
+ linker = "isolated"
package/check.ts CHANGED
@@ -63,6 +63,7 @@ export const checkTable = async function (): Promise<boolean> {
63
63
 
64
64
  // 收集所有表文件
65
65
  const allTableFiles: TableFileInfo[] = [];
66
+ let hasError = false;
66
67
 
67
68
  // 收集项目表字段定义文件
68
69
  for await (const file of tablesGlob.scan({
@@ -105,6 +106,7 @@ export const checkTable = async function (): Promise<boolean> {
105
106
  // 1) 文件名小驼峰校验
106
107
  if (!LOWER_CAMEL_CASE_REGEX.test(fileBaseName)) {
107
108
  Logger.warn(`${item.typeName}表 ${fileName} 文件名必须使用小驼峰命名(例如 testCustomers.json)`);
109
+ hasError = true;
108
110
  continue;
109
111
  }
110
112
 
@@ -116,12 +118,14 @@ export const checkTable = async function (): Promise<boolean> {
116
118
  for (const [colKey, fieldDef] of Object.entries(table)) {
117
119
  if (typeof fieldDef !== 'object' || fieldDef === null || Array.isArray(fieldDef)) {
118
120
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 规则必须为对象`);
121
+ hasError = true;
119
122
  continue;
120
123
  }
121
124
 
122
125
  // 检查是否使用了保留字段
123
126
  if (RESERVED_FIELDS.includes(colKey as any)) {
124
127
  Logger.warn(`${item.typeName}表 ${fileName} 文件包含保留字段 ${colKey},` + `不能在表定义中使用以下字段: ${RESERVED_FIELDS.join(', ')}`);
128
+ hasError = true;
125
129
  }
126
130
 
127
131
  // 直接使用字段对象
@@ -130,37 +134,47 @@ export const checkTable = async function (): Promise<boolean> {
130
134
  // 检查必填字段:name, type
131
135
  if (!field.name || typeof field.name !== 'string') {
132
136
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 缺少必填字段 name 或类型错误`);
137
+ hasError = true;
133
138
  continue;
134
139
  }
135
140
  if (!field.type || typeof field.type !== 'string') {
136
141
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 缺少必填字段 type 或类型错误`);
142
+ hasError = true;
137
143
  continue;
138
144
  }
139
145
 
140
146
  // 检查可选字段的类型
141
147
  if (field.min !== undefined && !(field.min === null || typeof field.min === 'number')) {
142
148
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 min 类型错误,必须为 null 或数字`);
149
+ hasError = true;
143
150
  }
144
151
  if (field.max !== undefined && !(field.max === null || typeof field.max === 'number')) {
145
152
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 max 类型错误,必须为 null 或数字`);
153
+ hasError = true;
146
154
  }
147
155
  if (field.detail !== undefined && typeof field.detail !== 'string') {
148
156
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 detail 类型错误,必须为字符串`);
157
+ hasError = true;
149
158
  }
150
159
  if (field.index !== undefined && typeof field.index !== 'boolean') {
151
160
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 index 类型错误,必须为布尔值`);
161
+ hasError = true;
152
162
  }
153
163
  if (field.unique !== undefined && typeof field.unique !== 'boolean') {
154
164
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 unique 类型错误,必须为布尔值`);
165
+ hasError = true;
155
166
  }
156
167
  if (field.nullable !== undefined && typeof field.nullable !== 'boolean') {
157
168
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 nullable 类型错误,必须为布尔值`);
169
+ hasError = true;
158
170
  }
159
171
  if (field.unsigned !== undefined && typeof field.unsigned !== 'boolean') {
160
172
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 unsigned 类型错误,必须为布尔值`);
173
+ hasError = true;
161
174
  }
162
175
  if (field.regexp !== undefined && field.regexp !== null && typeof field.regexp !== 'string') {
163
176
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段 regexp 类型错误,必须为 null 或字符串`);
177
+ hasError = true;
164
178
  }
165
179
 
166
180
  const { name: fieldName, type: fieldType, min: fieldMin, max: fieldMax, default: fieldDefault } = field;
@@ -168,17 +182,20 @@ export const checkTable = async function (): Promise<boolean> {
168
182
  // 字段名称必须为中文、数字、字母、下划线、短横线、空格
169
183
  if (!FIELD_NAME_REGEX.test(fieldName)) {
170
184
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段名称 "${fieldName}" 格式错误,` + `必须为中文、数字、字母、下划线、短横线、空格`);
185
+ hasError = true;
171
186
  }
172
187
 
173
188
  // 字段类型必须为string,number,text,array_string,array_text之一
174
189
  if (!FIELD_TYPES.includes(fieldType as any)) {
175
190
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 字段类型 "${fieldType}" 格式错误,` + `必须为${FIELD_TYPES.join('、')}之一`);
191
+ hasError = true;
176
192
  }
177
193
 
178
194
  // 约束:当最小值与最大值均为数字时,要求最小值 <= 最大值
179
195
  if (fieldMin !== undefined && fieldMax !== undefined && fieldMin !== null && fieldMax !== null) {
180
196
  if (fieldMin > fieldMax) {
181
197
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 最小值 "${fieldMin}" 不能大于最大值 "${fieldMax}"`);
198
+ hasError = true;
182
199
  }
183
200
  }
184
201
 
@@ -187,32 +204,39 @@ export const checkTable = async function (): Promise<boolean> {
187
204
  // text:min/max 应该为 null,默认值必须为 null
188
205
  if (fieldMin !== undefined && fieldMin !== null) {
189
206
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 的 text 类型最小值应为 null,当前为 "${fieldMin}"`);
207
+ hasError = true;
190
208
  }
191
209
  if (fieldMax !== undefined && fieldMax !== null) {
192
210
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 的 text 类型最大长度应为 null,当前为 "${fieldMax}"`);
211
+ hasError = true;
193
212
  }
194
213
  if (fieldDefault !== undefined && fieldDefault !== null) {
195
214
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 为 text 类型,默认值必须为 null,当前为 "${fieldDefault}"`);
215
+ hasError = true;
196
216
  }
197
217
  } else if (fieldType === 'string' || fieldType === 'array_string') {
198
218
  if (fieldMax !== undefined && (fieldMax === null || typeof fieldMax !== 'number')) {
199
219
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 为 ${fieldType} 类型,` + `最大长度必须为数字,当前为 "${fieldMax}"`);
220
+ hasError = true;
200
221
  } else if (fieldMax !== undefined && fieldMax > MAX_VARCHAR_LENGTH) {
201
222
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 最大长度 ${fieldMax} 越界,` + `${fieldType} 类型长度必须在 1..${MAX_VARCHAR_LENGTH} 范围内`);
223
+ hasError = true;
202
224
  }
203
225
  } else if (fieldType === 'number') {
204
226
  // number 类型:default 如果存在,必须为 null 或 number
205
227
  if (fieldDefault !== undefined && fieldDefault !== null && typeof fieldDefault !== 'number') {
206
228
  Logger.warn(`${item.typeName}表 ${fileName} 文件 ${colKey} 为 number 类型,` + `默认值必须为数字或 null,当前为 "${fieldDefault}"`);
229
+ hasError = true;
207
230
  }
208
231
  }
209
232
  }
210
233
  } catch (error: any) {
211
234
  Logger.error(`${item.typeName}表 ${fileName} 解析失败`, error);
235
+ hasError = true;
212
236
  }
213
237
  }
214
238
 
215
- return true;
239
+ return !hasError;
216
240
  } catch (error: any) {
217
241
  Logger.error('数据表定义检查过程中出错', error);
218
242
  return false;
package/env.ts CHANGED
@@ -22,7 +22,6 @@ const coreEnv: EnvConfig = {
22
22
  DEV_EMAIL: 'dev@qq.com',
23
23
  DEV_PASSWORD: '123456',
24
24
  BODY_LIMIT: 10485760, // 10MB
25
- PARAMS_CHECK: false,
26
25
 
27
26
  // ========== 日志配置 ==========
28
27
  LOG_DEBUG: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.8.8",
3
+ "version": "3.8.10",
4
4
  "description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -40,13 +40,9 @@
40
40
  "files": [
41
41
  "lib/",
42
42
  "loader/",
43
- "middleware/",
43
+ "plugins/",
44
44
  "router/",
45
45
  "types/",
46
- ".gitignore",
47
- ".npmignore",
48
- ".bunignore",
49
- ".editorconfig",
50
46
  ".npmrc",
51
47
  ".prettierignore",
52
48
  ".prettierrc",
@@ -56,11 +52,11 @@
56
52
  "main.ts",
57
53
  "check.ts",
58
54
  "env.ts",
59
- "menu.json",
60
55
  "paths.ts",
61
56
  "util.ts",
62
57
  "package.json",
63
- "README.md"
58
+ "README.md",
59
+ "LICENSE"
64
60
  ],
65
61
  "engines": {
66
62
  "bun": ">=1.3.0"
@@ -69,5 +65,5 @@
69
65
  "es-toolkit": "^1.41.0",
70
66
  "pathe": "^2.0.3"
71
67
  },
72
- "gitHead": "c8c0c2b5e7905cf3a01b77fd3d87b8f225fa9254"
68
+ "gitHead": "492e46d692787e6d141659fba27c26e030cd1181"
73
69
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 缓存插件 - TypeScript 版本
3
+ * 负责在服务器启动时缓存接口、菜单和角色权限到 Redis
4
+ */
5
+
6
+ import { CacheHelper } from '../lib/cacheHelper.js';
7
+ import type { Plugin } from '../types/plugin.js';
8
+ import type { BeflyContext } from '../types/befly.js';
9
+
10
+ /**
11
+ * 缓存插件
12
+ */
13
+ const cachePlugin: Plugin = {
14
+ name: '_cache',
15
+ after: ['_db', '_redis'],
16
+
17
+ async onInit(befly: BeflyContext): Promise<CacheHelper> {
18
+ return new CacheHelper(befly);
19
+ }
20
+ };
21
+
22
+ export default cachePlugin;
package/plugins/db.ts ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 数据库插件 - TypeScript 版本
3
+ * 初始化数据库连接和 SQL 管理器
4
+ */
5
+
6
+ import { Env } from '../env.js';
7
+ import { Logger } from '../lib/logger.js';
8
+ import { Database } from '../lib/database.js';
9
+ import { DbHelper } from '../lib/dbHelper.js';
10
+ import type { Plugin } from '../types/plugin.js';
11
+ import type { BeflyContext } from '../types/befly.js';
12
+
13
+ /**
14
+ * 数据库插件
15
+ */
16
+ const dbPlugin: Plugin = {
17
+ name: '_db',
18
+ after: ['_redis'],
19
+
20
+ async onInit(befly: BeflyContext): Promise<DbHelper | Record<string, never>> {
21
+ let sql: any = null;
22
+
23
+ try {
24
+ if (Env.DB_ENABLE === 1) {
25
+ // 创建 Bun SQL 客户端(内置连接池),并确保连接验证成功后再继续
26
+ // 从环境变量读取连接超时配置
27
+ const connectionTimeout = Env.DB_CONNECTION_TIMEOUT ? parseInt(Env.DB_CONNECTION_TIMEOUT) : 30000;
28
+
29
+ sql = await Database.connectSql({
30
+ connectionTimeout
31
+ });
32
+
33
+ // 创建数据库管理器实例,直接传入 sql 对象
34
+ const dbManager = new DbHelper(befly, sql);
35
+
36
+ return dbManager;
37
+ } else {
38
+ Logger.warn('数据库未启用(DB_ENABLE≠1),跳过初始化');
39
+ return {};
40
+ }
41
+ } catch (error: any) {
42
+ Logger.error('数据库初始化失败', error);
43
+
44
+ // 清理资源
45
+ if (sql) {
46
+ try {
47
+ await sql.close();
48
+ } catch (cleanupError: any) {
49
+ Logger.error('清理连接池失败:', cleanupError);
50
+ }
51
+ }
52
+
53
+ // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
54
+ throw error;
55
+ }
56
+ }
57
+ };
58
+
59
+ export default dbPlugin;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 日志插件 - TypeScript 版本
3
+ * 提供全局日志功能
4
+ */
5
+
6
+ import { Logger } from '../lib/logger.js';
7
+ import type { Plugin } from '../types/plugin.js';
8
+ import type { BeflyContext } from '../types/befly.js';
9
+
10
+ /**
11
+ * 日志插件
12
+ */
13
+ const loggerPlugin: Plugin = {
14
+ name: '_logger',
15
+ after: [],
16
+
17
+ async onInit(befly: BeflyContext): Promise<typeof Logger> {
18
+ try {
19
+ return Logger;
20
+ } catch (error: any) {
21
+ // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
22
+ throw error;
23
+ }
24
+ }
25
+ };
26
+
27
+ export default loggerPlugin;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Redis 插件 - TypeScript 版本
3
+ * 初始化 Redis 连接和助手工具
4
+ */
5
+
6
+ import { Env } from '../env.js';
7
+ import { Logger } from '../lib/logger.js';
8
+ import { RedisHelper } from '../lib/redisHelper.js';
9
+ import { Database } from '../lib/database.js';
10
+ import type { Plugin } from '../types/plugin.js';
11
+ import type { BeflyContext } from '../types/befly.js';
12
+
13
+ /**
14
+ * Redis 插件
15
+ */
16
+ const redisPlugin: Plugin = {
17
+ name: '_redis',
18
+ after: ['_logger'],
19
+
20
+ async onInit(befly: BeflyContext): Promise<RedisHelper | Record<string, never>> {
21
+ try {
22
+ if (Env.REDIS_ENABLE === 1) {
23
+ // 初始化 Redis 客户端(统一使用 database.ts 的连接管理)
24
+ await Database.connectRedis();
25
+
26
+ // 返回 RedisHelper 实例
27
+ return new RedisHelper();
28
+ } else {
29
+ Logger.warn('Redis 未启用,跳过初始化');
30
+ return {};
31
+ }
32
+ } catch (error: any) {
33
+ Logger.error('Redis 初始化失败', error);
34
+
35
+ // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
36
+ throw error;
37
+ }
38
+ }
39
+ };
40
+
41
+ export default redisPlugin;