befly 2.3.0 → 2.3.2

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/checks/table.js CHANGED
@@ -56,9 +56,19 @@ export const checkTable = async () => {
56
56
  for (const { file, type } of allTableFiles) {
57
57
  totalFiles++;
58
58
  const fileName = path.basename(file);
59
+ const fileBaseName = path.basename(file, '.json');
59
60
  const fileType = type === 'core' ? '内核' : '项目';
60
61
 
61
62
  try {
63
+ // 1) 文件名小驼峰校验:必须以小写字母开头,后续可包含小写/数字,或多个 [大写+小写/数字] 片段
64
+ // 示例:userTable、testCustomers、common
65
+ const lowerCamelCaseRegex = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
66
+ if (!lowerCamelCaseRegex.test(fileBaseName)) {
67
+ Logger.error(`${fileType}表 ${fileName} 文件名必须使用小驼峰命名(例如 testCustomers.json)`);
68
+ // 命名不合规,直接标记为无效文件
69
+ throw new Error('文件命名不符合小驼峰规范');
70
+ }
71
+
62
72
  // 读取并解析 JSON 文件
63
73
  const table = await Bun.file(file).json();
64
74
  let fileValid = true;
package/main.js CHANGED
@@ -43,11 +43,20 @@ class Befly {
43
43
  const singleCheckStart = Bun.nanoseconds();
44
44
 
45
45
  // 导入检查模块
46
- const check = await import(file);
46
+ const checkModule = await import(file);
47
+
48
+ // 仅允许具名导出(以 check 开头)的检查函数
49
+ let checkFn = null;
50
+ for (const [exportName, exportValue] of Object.entries(checkModule)) {
51
+ if (typeof exportValue === 'function' && /^check/i.test(exportName)) {
52
+ checkFn = exportValue;
53
+ break;
54
+ }
55
+ }
47
56
 
48
- // 执行默认导出的函数
49
- if (typeof check.default === 'function') {
50
- const checkResult = await check.default(this.appContext);
57
+ // 执行检查函数
58
+ if (typeof checkFn === 'function') {
59
+ const checkResult = await checkFn(this.appContext);
51
60
  const singleCheckTime = calcPerfTime(singleCheckStart);
52
61
 
53
62
  if (checkResult === true) {
@@ -59,7 +68,7 @@ class Befly {
59
68
  }
60
69
  } else {
61
70
  const singleCheckTime = calcPerfTime(singleCheckStart);
62
- Logger.warn(`文件 ${fileName} 未导出默认函数,耗时: ${singleCheckTime}`);
71
+ Logger.warn(`文件 ${fileName} 未找到可执行的检查函数(必须具名导出以 check 开头的函数),耗时: ${singleCheckTime}`);
63
72
  failedChecks++;
64
73
  }
65
74
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
package/scripts/syncDb.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import path from 'node:path';
6
6
  import { Env } from '../config/env.js';
7
7
  import { Logger } from '../utils/logger.js';
8
- import { parseFieldRule, createSqlClient } from '../utils/index.js';
8
+ import { parseFieldRule, createSqlClient, toSnakeTableName } from '../utils/index.js';
9
9
  import { __dirtables, getProjectDir } from '../system.js';
10
10
  import { checkTable } from '../checks/table.js';
11
11
 
@@ -16,6 +16,8 @@ const typeMapping = {
16
16
  array: 'VARCHAR'
17
17
  };
18
18
 
19
+ // 表名转换函数已移动至 utils/index.js 的 toSnakeTableName
20
+
19
21
  // 环境开关读取(支持未在 Env 显式声明的变量,默认值兜底)
20
22
  const getFlag = (val, def = 0) => {
21
23
  if (val === undefined || val === null || val === '') return !!def;
@@ -466,7 +468,8 @@ const SyncDb = async () => {
466
468
  for (const dir of directories) {
467
469
  try {
468
470
  for await (const file of tablesGlob.scan({ cwd: dir, absolute: true, onlyFiles: true })) {
469
- const tableName = path.basename(file, '.json');
471
+ const fileBaseName = path.basename(file, '.json');
472
+ const tableName = toSnakeTableName(fileBaseName);
470
473
  const tableDefinition = await Bun.file(file).json();
471
474
  const result = await exec(client, 'SELECT COUNT(*) as count FROM information_schema.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?', [Env.MYSQL_DB || 'test', tableName]);
472
475
  const exists = result[0].count > 0;
package/utils/index.js CHANGED
@@ -93,6 +93,9 @@ export const isType = (value, type) => {
93
93
 
94
94
  // 语义类型单独处理,其余走 actualType === expectedType
95
95
  switch (expectedType) {
96
+ case 'function':
97
+ // 统一将普通函数、异步函数、生成器函数等都识别为函数
98
+ return typeof value === 'function';
96
99
  case 'nan':
97
100
  return typeof value === 'number' && Number.isNaN(value);
98
101
  case 'empty':
@@ -294,6 +297,16 @@ export const validateRegex = (value) => {
294
297
  }
295
298
  };
296
299
 
300
+ // 将 lowerCamelCase 或单词形式转换为下划线风格(snake_case)
301
+ // 例如:userTable -> user_table, testNewFormat -> test_new_format, users -> users, orderV2 -> order_v2
302
+ export const toSnakeTableName = (name) => {
303
+ if (!name) return name;
304
+ return String(name)
305
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
306
+ .replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, '$1_$2')
307
+ .toLowerCase();
308
+ };
309
+
297
310
  // 专门用于处理⚡分隔的字段规则
298
311
  export const parseFieldRule = (rule) => {
299
312
  const allParts = rule.split('⚡');