befly 3.8.27 → 3.8.30

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 (65) hide show
  1. package/README.md +8 -6
  2. package/checks/checkApi.ts +2 -1
  3. package/checks/checkTable.ts +3 -2
  4. package/hooks/parser.ts +5 -3
  5. package/hooks/permission.ts +12 -5
  6. package/lib/cacheHelper.ts +76 -62
  7. package/lib/connect.ts +8 -35
  8. package/lib/dbHelper.ts +14 -11
  9. package/lib/jwt.ts +58 -437
  10. package/lib/logger.ts +76 -197
  11. package/lib/redisHelper.ts +163 -1
  12. package/lib/sqlBuilder.ts +2 -1
  13. package/lib/validator.ts +9 -8
  14. package/loader/loadApis.ts +4 -7
  15. package/loader/loadHooks.ts +2 -2
  16. package/loader/loadPlugins.ts +4 -4
  17. package/main.ts +4 -17
  18. package/package.json +10 -9
  19. package/paths.ts +0 -6
  20. package/plugins/db.ts +2 -2
  21. package/plugins/jwt.ts +5 -5
  22. package/plugins/redis.ts +1 -1
  23. package/router/api.ts +2 -2
  24. package/router/static.ts +1 -2
  25. package/sync/syncAll.ts +2 -2
  26. package/sync/syncApi.ts +10 -7
  27. package/sync/syncDb/apply.ts +11 -11
  28. package/sync/syncDb/constants.ts +61 -12
  29. package/sync/syncDb/ddl.ts +7 -7
  30. package/sync/syncDb/helpers.ts +3 -3
  31. package/sync/syncDb/schema.ts +16 -19
  32. package/sync/syncDb/table.ts +6 -5
  33. package/sync/syncDb/tableCreate.ts +7 -7
  34. package/sync/syncDb/types.ts +3 -2
  35. package/sync/syncDb/version.ts +4 -4
  36. package/sync/syncDb.ts +11 -10
  37. package/sync/syncDev.ts +10 -48
  38. package/sync/syncMenu.ts +11 -8
  39. package/tests/cacheHelper.test.ts +327 -0
  40. package/tests/dbHelper-columns.test.ts +5 -20
  41. package/tests/dbHelper-execute.test.ts +14 -68
  42. package/tests/fields-redis-cache.test.ts +5 -3
  43. package/tests/integration.test.ts +15 -26
  44. package/tests/jwt.test.ts +36 -94
  45. package/tests/logger.test.ts +32 -34
  46. package/tests/redisHelper.test.ts +270 -0
  47. package/tests/redisKeys.test.ts +76 -0
  48. package/tests/sync-connection.test.ts +0 -6
  49. package/tests/syncDb-apply.test.ts +3 -2
  50. package/tests/syncDb-constants.test.ts +15 -14
  51. package/tests/syncDb-ddl.test.ts +3 -2
  52. package/tests/syncDb-helpers.test.ts +3 -2
  53. package/tests/syncDb-schema.test.ts +3 -3
  54. package/tests/syncDb-types.test.ts +3 -2
  55. package/tests/util.test.ts +5 -1
  56. package/types/befly.d.ts +2 -15
  57. package/types/common.d.ts +11 -93
  58. package/types/database.d.ts +216 -5
  59. package/types/index.ts +1 -0
  60. package/types/logger.d.ts +11 -41
  61. package/types/table.d.ts +213 -0
  62. package/hooks/_rateLimit.ts +0 -64
  63. package/lib/regexAliases.ts +0 -59
  64. package/lib/xml.ts +0 -383
  65. package/tests/xml.test.ts +0 -101
package/main.ts CHANGED
@@ -4,12 +4,11 @@
4
4
  */
5
5
 
6
6
  // ========== 外部依赖 ==========
7
- import { calcPerfTime } from 'befly-util';
7
+ import { calcPerfTime } from 'befly-shared/calcPerfTime';
8
8
 
9
9
  // ========== 相对导入 ==========
10
10
  import { Logger } from './lib/logger.js';
11
11
  import { Cipher } from './lib/cipher.js';
12
- import { Jwt } from './lib/jwt.js';
13
12
  import { Connect } from './lib/connect.js';
14
13
  import { DbHelper } from './lib/dbHelper.js';
15
14
  import { RedisHelper } from './lib/redisHelper.js';
@@ -103,7 +102,7 @@ export class Befly {
103
102
  '/*': staticHandler(this.config)
104
103
  },
105
104
  error: (error: Error) => {
106
- Logger.error('服务启动时发生错误', error);
105
+ Logger.error({ err: error }, '服务启动时发生错误');
107
106
  return Response.json({ code: 1, msg: '内部服务器错误' });
108
107
  }
109
108
  });
@@ -128,7 +127,7 @@ export class Befly {
128
127
  await Connect.disconnect();
129
128
  Logger.info('数据库连接已关闭');
130
129
  } catch (error: any) {
131
- Logger.error('关闭数据库连接时出错:', error);
130
+ Logger.error({ err: error }, '关闭数据库连接时出错');
132
131
  }
133
132
 
134
133
  Logger.info('服务器已优雅关闭');
@@ -140,20 +139,8 @@ export class Befly {
140
139
 
141
140
  return server;
142
141
  } catch (error: any) {
143
- Logger.error('项目启动失败', error);
142
+ Logger.error({ err: error }, '项目启动失败');
144
143
  process.exit(1);
145
144
  }
146
145
  }
147
146
  }
148
-
149
- // 核心类和工具导出
150
- export {
151
- // 配置
152
- Logger,
153
- Cipher,
154
- Jwt,
155
- Database,
156
- DbHelper,
157
- RedisHelper,
158
- coreDir
159
- };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.8.27",
3
+ "version": "3.8.30",
4
4
  "description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -15,10 +15,7 @@
15
15
  "types": "./main.ts",
16
16
  "default": "./main.ts"
17
17
  },
18
- "./types": {
19
- "types": "./types/index.ts",
20
- "default": "./types/index.ts"
21
- }
18
+ "./*": "./*"
22
19
  },
23
20
  "scripts": {
24
21
  "bundler": "bun build ./main.ts --outfile ./main.single.ts --minify --target bun"
@@ -66,12 +63,16 @@
66
63
  "bun": ">=1.3.0"
67
64
  },
68
65
  "dependencies": {
69
- "befly-util": "^1.0.6",
66
+ "befly-shared": "^1.1.1",
70
67
  "chalk": "^5.6.2",
71
- "es-toolkit": "^1.41.0",
72
- "pathe": "^2.0.3"
68
+ "es-toolkit": "^1.42.0",
69
+ "fast-jwt": "^6.0.2",
70
+ "fast-xml-parser": "^5.3.2",
71
+ "pathe": "^2.0.3",
72
+ "pino": "^10.1.0",
73
+ "pino-roll": "^4.0.0"
73
74
  },
74
- "gitHead": "54c0f11f65a56f7df036fdf386b72329496777b4",
75
+ "gitHead": "2031550167896390ac58c50aeb52b8c23426844e",
75
76
  "devDependencies": {
76
77
  "typescript": "^5.9.3"
77
78
  }
package/paths.ts CHANGED
@@ -46,9 +46,6 @@ export const corePluginDir = join(__dirname, 'plugins');
46
46
  */
47
47
  export const coreHookDir = join(__dirname, 'hooks');
48
48
 
49
- /**
50
- * Core 框架 API 目录
51
-
52
49
  /**
53
50
  * Core 框架 API 目录
54
51
  * @description packages/core/apis/
@@ -93,9 +90,6 @@ export const projectPluginDir = join(projectDir, 'plugins');
93
90
  */
94
91
  export const projectHookDir = join(projectDir, 'hooks');
95
92
 
96
- /**
97
- * 项目 API 目录
98
-
99
93
  /**
100
94
  * 项目 API 目录
101
95
  * @description {projectDir}/apis/
package/plugins/db.ts CHANGED
@@ -26,14 +26,14 @@ const dbPlugin: Plugin = {
26
26
 
27
27
  return dbManager;
28
28
  } catch (error: any) {
29
- Logger.error('数据库初始化失败', error);
29
+ Logger.error({ err: error }, '数据库初始化失败');
30
30
 
31
31
  // 清理资源
32
32
  if (sql) {
33
33
  try {
34
34
  await sql.close();
35
35
  } catch (cleanupError: any) {
36
- Logger.error('清理连接池失败:', cleanupError);
36
+ Logger.error({ err: cleanupError }, '清理连接池失败');
37
37
  }
38
38
  }
39
39
 
package/plugins/jwt.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  /**
2
2
  * JWT 插件
3
- * 提供 JSON Web Token 签名和验证功能
4
3
  */
5
4
 
6
5
  import { Jwt } from '../lib/jwt.js';
7
6
 
8
7
  import type { Plugin } from '../types/plugin.js';
9
8
 
10
- export default {
11
- name: 'jwt',
9
+ const jwtPlugin: Plugin = {
12
10
  handler: (context, config) => {
13
- return Jwt;
11
+ return new Jwt(config?.auth);
14
12
  }
15
- } as Plugin;
13
+ };
14
+
15
+ export default jwtPlugin;
package/plugins/redis.ts CHANGED
@@ -26,7 +26,7 @@ const redisPlugin: Plugin = {
26
26
  // 返回 RedisHelper 实例
27
27
  return new RedisHelper(config.prefix);
28
28
  } catch (error: any) {
29
- Logger.error('Redis 初始化失败', error);
29
+ Logger.error({ err: error }, 'Redis 初始化失败');
30
30
 
31
31
  // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
32
32
  throw error;
package/router/api.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  // 外部依赖
7
- import { genShortId } from 'befly-util';
7
+ import { genShortId } from 'befly-shared/genShortId';
8
8
 
9
9
  // 相对导入
10
10
  import { FinalResponse } from '../util.js';
@@ -88,7 +88,7 @@ export function apiHandler(apis: Map<string, ApiRoute>, hooks: Hook[], context:
88
88
  } catch (err: any) {
89
89
  // 全局错误处理
90
90
  const errorPath = ctx.api ? apiPath : req.url;
91
- Logger.error(`请求错误: ${errorPath}`, err);
91
+ Logger.error({ err: err, path: errorPath }, '请求错误');
92
92
  ctx.result = {
93
93
  code: 1,
94
94
  msg: '内部服务错误'
package/router/static.ts CHANGED
@@ -55,8 +55,7 @@ export function staticHandler(config: BeflyOptions) {
55
55
  );
56
56
  }
57
57
  } catch (error: any) {
58
- // 记录详细的错误日志
59
- Logger.error('静态文件处理失败', error);
58
+ Logger.error({ err: error }, '静态文件处理失败');
60
59
 
61
60
  return Response.json(
62
61
  {
package/sync/syncAll.ts CHANGED
@@ -32,9 +32,9 @@ export async function syncAllCommand(config: BeflyOptions, options: SyncOptions
32
32
 
33
33
  // 输出总结
34
34
  const totalTimeSeconds = ((Date.now() - startTime) / 1000).toFixed(2);
35
- Logger.info(`同步完成 (耗时 ${totalTimeSeconds}s)`);
35
+ Logger.info({ duration: totalTimeSeconds }, '同步完成');
36
36
  } catch (error: any) {
37
- Logger.error('同步过程中发生错误', error);
37
+ Logger.error({ err: error }, '同步过程中发生错误');
38
38
  throw error;
39
39
  }
40
40
  }
package/sync/syncApi.ts CHANGED
@@ -13,8 +13,11 @@
13
13
  import { readdirSync, statSync } from 'node:fs';
14
14
  import { join, dirname, relative, basename } from 'pathe';
15
15
  import { Connect } from '../lib/connect.js';
16
+ import { DbHelper } from '../lib/dbHelper.js';
16
17
  import { RedisHelper } from '../lib/redisHelper.js';
17
- import { scanFiles, scanAddons, addonDirExists, getAddonDir } from 'befly-util';
18
+ import { RedisKeys } from 'befly-shared/redisKeys';
19
+ import { scanFiles } from 'befly-shared/scanFiles';
20
+ import { scanAddons, addonDirExists, getAddonDir } from 'befly-shared/addonHelper';
18
21
 
19
22
  import { Logger } from '../lib/logger.js';
20
23
  import { projectDir } from '../paths.js';
@@ -59,7 +62,7 @@ async function extractApiInfo(filePath: string, apiRoot: string, type: 'app' | '
59
62
  addonTitle: addonTitle || addonName
60
63
  };
61
64
  } catch (error: any) {
62
- Logger.error('同步 API 失败:', error);
65
+ Logger.error({ err: error }, '同步 API 失败');
63
66
  throw error;
64
67
  }
65
68
  }
@@ -136,7 +139,7 @@ async function scanAllApis(projectRoot: string): Promise<ApiInfo[]> {
136
139
 
137
140
  return apis;
138
141
  } catch (error: any) {
139
- Logger.error(`接口扫描失败:`, error);
142
+ Logger.error({ err: error }, '接口扫描失败');
140
143
  return apis;
141
144
  }
142
145
  }
@@ -184,7 +187,7 @@ async function syncApis(helper: any, apis: ApiInfo[]): Promise<void> {
184
187
  });
185
188
  }
186
189
  } catch (error: any) {
187
- Logger.error(`同步接口 "${api.name}" 失败:`, error);
190
+ Logger.error({ err: error, api: api.name }, '同步接口失败');
188
191
  }
189
192
  }
190
193
  }
@@ -222,7 +225,7 @@ export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptio
222
225
  // 连接数据库(SQL + Redis)
223
226
  await Connect.connect(config);
224
227
 
225
- const helper = Connect.getDbHelper();
228
+ const helper = new DbHelper({ redis: new RedisHelper() } as any, Connect.getSql());
226
229
 
227
230
  // 1. 检查表是否存在(addon_admin_api 来自 addon-admin 组件)
228
231
  const exists = await helper.tableExists('addon_admin_api');
@@ -251,12 +254,12 @@ export async function syncApiCommand(config: BeflyOptions, options: SyncApiOptio
251
254
  });
252
255
 
253
256
  const redisHelper = new RedisHelper();
254
- await redisHelper.setObject('apis:all', apiList);
257
+ await redisHelper.setObject(RedisKeys.apisAll(), apiList);
255
258
  } catch (error: any) {
256
259
  // 忽略缓存错误
257
260
  }
258
261
  } catch (error: any) {
259
- Logger.error('API 同步失败:', error);
262
+ Logger.error({ err: error }, 'API 同步失败');
260
263
  throw error;
261
264
  } finally {
262
265
  await Connect.disconnect();
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import { Logger } from '../../lib/logger.js';
10
- import { IS_MYSQL, IS_PG, IS_SQLITE, IS_PLAN, CHANGE_TYPE_LABELS, typeMapping } from './constants.js';
10
+ import { isMySQL, isPG, isSQLite, IS_PLAN, CHANGE_TYPE_LABELS, getTypeMapping } from './constants.js';
11
11
  import { logFieldChange, resolveDefaultValue, isStringOrArrayType } from './helpers.js';
12
12
  import { executeDDLSafely, buildIndexSQL } from './ddl.js';
13
13
  import { rebuildSqliteTable } from './sqlite.js';
@@ -27,7 +27,7 @@ import type { FieldDefinition } from 'befly/types/common';
27
27
  * @returns 完整的 ALTER TABLE 语句
28
28
  */
29
29
  function buildAlterTableSQL(tableName: string, clauses: string[]): string {
30
- if (IS_MYSQL) {
30
+ if (isMySQL()) {
31
31
  return `ALTER TABLE \`${tableName}\` ${clauses.join(', ')}, ALGORITHM=INSTANT, LOCK=NONE`;
32
32
  }
33
33
  return `ALTER TABLE "${tableName}" ${clauses.join(', ')}`;
@@ -51,7 +51,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
51
51
  const changes: FieldChange[] = [];
52
52
 
53
53
  // 检查长度变化(string和array类型) - SQLite 不比较长度
54
- if (!IS_SQLITE && isStringOrArrayType(fieldDef.type)) {
54
+ if (!isSQLite() && isStringOrArrayType(fieldDef.type)) {
55
55
  if (existingColumn.max !== fieldDef.max) {
56
56
  changes.push({
57
57
  type: 'length',
@@ -62,7 +62,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
62
62
  }
63
63
 
64
64
  // 检查注释变化(MySQL/PG 支持列注释)
65
- if (!IS_SQLITE) {
65
+ if (!isSQLite()) {
66
66
  const currentComment = existingColumn.comment || '';
67
67
  if (currentComment !== fieldDef.name) {
68
68
  changes.push({
@@ -74,6 +74,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
74
74
  }
75
75
 
76
76
  // 检查数据类型变化(只对比基础类型)
77
+ const typeMapping = getTypeMapping();
77
78
  const expectedType = typeMapping[fieldDef.type].toLowerCase();
78
79
  const currentType = existingColumn.type.toLowerCase();
79
80
 
@@ -127,7 +128,7 @@ export async function applyTablePlan(sql: SQL, tableName: string, fields: Record
127
128
  if (!plan || !plan.changed) return;
128
129
 
129
130
  // SQLite: 仅支持部分 ALTER;需要时走重建
130
- if (IS_SQLITE) {
131
+ if (isSQLite()) {
131
132
  if (plan.modifyClauses.length > 0 || plan.defaultClauses.length > 0) {
132
133
  if (IS_PLAN) Logger.debug(`[计划] 重建表 ${tableName} 以应用列修改/默认值变化`);
133
134
  else await rebuildSqliteTable(sql, tableName, fields);
@@ -143,19 +144,19 @@ export async function applyTablePlan(sql: SQL, tableName: string, fields: Record
143
144
  if (clauses.length > 0) {
144
145
  const stmt = buildAlterTableSQL(tableName, clauses);
145
146
  if (IS_PLAN) Logger.debug(`[计划] ${stmt}`);
146
- else if (IS_MYSQL) await executeDDLSafely(sql, stmt);
147
+ else if (isMySQL()) await executeDDLSafely(sql, stmt);
147
148
  else await sql.unsafe(stmt);
148
149
  }
149
150
  }
150
151
 
151
152
  // 默认值专用 ALTER(SQLite 不支持)
152
153
  if (plan.defaultClauses.length > 0) {
153
- if (IS_SQLITE) {
154
+ if (isSQLite()) {
154
155
  Logger.warn(`SQLite 不支持修改默认值,表 ${tableName} 的默认值变更已跳过`);
155
156
  } else {
156
157
  const stmt = buildAlterTableSQL(tableName, plan.defaultClauses);
157
158
  if (IS_PLAN) Logger.debug(`[计划] ${stmt}`);
158
- else if (IS_MYSQL) await executeDDLSafely(sql, stmt);
159
+ else if (isMySQL()) await executeDDLSafely(sql, stmt);
159
160
  else await sql.unsafe(stmt);
160
161
  }
161
162
  }
@@ -174,15 +175,14 @@ export async function applyTablePlan(sql: SQL, tableName: string, fields: Record
174
175
  Logger.debug(`[索引变化] 删除索引 ${tableName}.${act.indexName} (${act.fieldName})`);
175
176
  }
176
177
  } catch (error: any) {
177
- Logger.error(`${act.action === 'create' ? '创建' : '删除'}索引失败`, error);
178
- Logger.warn(`表名: ${tableName}, 索引名: ${act.indexName}, 字段: ${act.fieldName}`);
178
+ Logger.error({ err: error, table: tableName, index: act.indexName, field: act.fieldName }, `${act.action === 'create' ? '创建' : '删除'}索引失败`);
179
179
  throw error;
180
180
  }
181
181
  }
182
182
  }
183
183
 
184
184
  // PG 列注释
185
- if (IS_PG && plan.commentActions && plan.commentActions.length > 0) {
185
+ if (isPG() && plan.commentActions && plan.commentActions.length > 0) {
186
186
  for (const stmt of plan.commentActions) {
187
187
  if (IS_PLAN) Logger.info(`[计划] ${stmt}`);
188
188
  else await sql.unsafe(stmt);
@@ -63,17 +63,66 @@ export const MYSQL_TABLE_CONFIG = {
63
63
  // 是否为计划模式(仅输出 SQL 不执行)
64
64
  export const IS_PLAN = process.argv.includes('--plan');
65
65
 
66
- // 数据库类型判断
67
- export const DB = (process.env.DB_TYPE || 'mysql').toLowerCase();
68
- export const IS_MYSQL = DB === 'mysql';
69
- export const IS_PG = DB === 'postgresql' || DB === 'postgres';
70
- export const IS_SQLITE = DB === 'sqlite';
66
+ // 数据库类型(运行时设置,默认 mysql)
67
+ let _dbType: string = 'mysql';
71
68
 
72
- // 字段类型映射(按方言)
73
- export const typeMapping = {
74
- number: IS_SQLITE ? 'INTEGER' : IS_PG ? 'BIGINT' : 'BIGINT',
75
- string: IS_SQLITE ? 'TEXT' : IS_PG ? 'character varying' : 'VARCHAR',
76
- text: IS_MYSQL ? 'MEDIUMTEXT' : 'TEXT',
77
- array_string: IS_SQLITE ? 'TEXT' : IS_PG ? 'character varying' : 'VARCHAR',
78
- array_text: IS_MYSQL ? 'MEDIUMTEXT' : 'TEXT'
69
+ /**
70
+ * 设置数据库类型(由 syncDbCommand 调用)
71
+ * @param dbType - 数据库类型(mysql/postgresql/postgres/sqlite)
72
+ */
73
+ export function setDbType(dbType: string): void {
74
+ _dbType = (dbType || 'mysql').toLowerCase();
75
+ }
76
+
77
+ /**
78
+ * 获取当前数据库类型
79
+ */
80
+ export function getDbType(): string {
81
+ return _dbType;
82
+ }
83
+
84
+ // 数据库类型判断(getter 函数,运行时动态计算)
85
+ export function isMySQL(): boolean {
86
+ return _dbType === 'mysql';
87
+ }
88
+
89
+ export function isPG(): boolean {
90
+ return _dbType === 'postgresql' || _dbType === 'postgres';
91
+ }
92
+
93
+ export function isSQLite(): boolean {
94
+ return _dbType === 'sqlite';
95
+ }
96
+
97
+ // 兼容旧代码的静态别名(通过 getter 实现动态获取)
98
+ export const DB_TYPE = {
99
+ get current(): string {
100
+ return _dbType;
101
+ },
102
+ get IS_MYSQL(): boolean {
103
+ return isMySQL();
104
+ },
105
+ get IS_PG(): boolean {
106
+ return isPG();
107
+ },
108
+ get IS_SQLITE(): boolean {
109
+ return isSQLite();
110
+ }
79
111
  };
112
+
113
+ /**
114
+ * 获取字段类型映射(根据当前数据库类型)
115
+ */
116
+ export function getTypeMapping(): Record<string, string> {
117
+ const isSqlite = isSQLite();
118
+ const isPg = isPG();
119
+ const isMysql = isMySQL();
120
+
121
+ return {
122
+ number: isSqlite ? 'INTEGER' : isPg ? 'BIGINT' : 'BIGINT',
123
+ string: isSqlite ? 'TEXT' : isPg ? 'character varying' : 'VARCHAR',
124
+ text: isMysql ? 'MEDIUMTEXT' : 'TEXT',
125
+ array_string: isSqlite ? 'TEXT' : isPg ? 'character varying' : 'VARCHAR',
126
+ array_text: isMysql ? 'MEDIUMTEXT' : 'TEXT'
127
+ };
128
+ }
@@ -10,7 +10,7 @@
10
10
 
11
11
  import { snakeCase } from 'es-toolkit/string';
12
12
  import { Logger } from '../../lib/logger.js';
13
- import { IS_MYSQL, IS_PG, typeMapping } from './constants.js';
13
+ import { isMySQL, isPG, getTypeMapping } from './constants.js';
14
14
  import { quoteIdentifier, escapeComment } from './helpers.js';
15
15
  import { resolveDefaultValue, generateDefaultSql, getSqlType } from './types.js';
16
16
 
@@ -31,7 +31,7 @@ export function buildIndexSQL(tableName: string, indexName: string, fieldName: s
31
31
  const indexQuoted = quoteIdentifier(indexName);
32
32
  const fieldQuoted = quoteIdentifier(fieldName);
33
33
 
34
- if (IS_MYSQL) {
34
+ if (isMySQL()) {
35
35
  const parts = [];
36
36
  if (action === 'create') {
37
37
  parts.push(`ADD INDEX ${indexQuoted} (${fieldQuoted})`);
@@ -44,7 +44,7 @@ export function buildIndexSQL(tableName: string, indexName: string, fieldName: s
44
44
  return `ALTER TABLE ${tableQuoted} ${parts.join(', ')}`;
45
45
  }
46
46
 
47
- if (IS_PG) {
47
+ if (isPG()) {
48
48
  if (action === 'create') {
49
49
  // 始终使用 CONCURRENTLY
50
50
  return `CREATE INDEX CONCURRENTLY IF NOT EXISTS ${indexQuoted} ON ${tableQuoted}(${fieldQuoted})`;
@@ -65,7 +65,7 @@ export function buildIndexSQL(tableName: string, indexName: string, fieldName: s
65
65
  * @returns 系统字段的列定义数组
66
66
  */
67
67
  export function buildSystemColumnDefs(): string[] {
68
- if (IS_MYSQL) {
68
+ if (isMySQL()) {
69
69
  return ['`id` BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT COMMENT "主键ID"', '`created_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "创建时间"', '`updated_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "更新时间"', '`deleted_at` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "删除时间"', '`state` BIGINT UNSIGNED NOT NULL DEFAULT 0 COMMENT "状态字段"'];
70
70
  }
71
71
  return ['"id" INTEGER PRIMARY KEY', '"created_at" INTEGER NOT NULL DEFAULT 0', '"updated_at" INTEGER NOT NULL DEFAULT 0', '"deleted_at" INTEGER NOT NULL DEFAULT 0', '"state" INTEGER NOT NULL DEFAULT 0'];
@@ -94,7 +94,7 @@ export function buildBusinessColumnDefs(fields: Record<string, FieldDefinition>)
94
94
  const uniqueSql = fieldDef.unique ? ' UNIQUE' : '';
95
95
  const nullableSql = fieldDef.nullable ? ' NULL' : ' NOT NULL';
96
96
 
97
- if (IS_MYSQL) {
97
+ if (isMySQL()) {
98
98
  colDefs.push(`\`${dbFieldName}\` ${sqlType}${uniqueSql}${nullableSql}${defaultSql} COMMENT "${escapeComment(fieldDef.name)}"`);
99
99
  } else {
100
100
  colDefs.push(`"${dbFieldName}" ${sqlType}${uniqueSql}${nullableSql}${defaultSql}`);
@@ -126,10 +126,10 @@ export function generateDDLClause(fieldKey: string, fieldDef: FieldDefinition, i
126
126
  const uniqueSql = fieldDef.unique ? ' UNIQUE' : '';
127
127
  const nullableSql = fieldDef.nullable ? ' NULL' : ' NOT NULL';
128
128
 
129
- if (IS_MYSQL) {
129
+ if (isMySQL()) {
130
130
  return `${isAdd ? 'ADD COLUMN' : 'MODIFY COLUMN'} \`${dbFieldName}\` ${sqlType}${uniqueSql}${nullableSql}${defaultSql} COMMENT "${escapeComment(fieldDef.name)}"`;
131
131
  }
132
- if (IS_PG) {
132
+ if (isPG()) {
133
133
  if (isAdd) return `ADD COLUMN IF NOT EXISTS "${dbFieldName}" ${sqlType}${uniqueSql}${nullableSql}${defaultSql}`;
134
134
  // PG 修改:类型与非空可分条执行,生成 TYPE 改变;非空另由上层统一控制
135
135
  return `ALTER COLUMN "${dbFieldName}" TYPE ${sqlType}`;
@@ -7,7 +7,7 @@
7
7
  * - 字段默认值应用
8
8
  */
9
9
 
10
- import { IS_MYSQL, IS_PG } from './constants.js';
10
+ import { isMySQL, isPG } from './constants.js';
11
11
  import { Logger } from '../../lib/logger.js';
12
12
 
13
13
  // 从 types.ts 重新导出,保持向后兼容
@@ -26,8 +26,8 @@ export { isStringOrArrayType, getSqlType, resolveDefaultValue, generateDefaultSq
26
26
  * // SQLite: user_table
27
27
  */
28
28
  export function quoteIdentifier(identifier: string): string {
29
- if (IS_MYSQL) return `\`${identifier}\``;
30
- if (IS_PG) return `"${identifier}"`;
29
+ if (isMySQL()) return `\`${identifier}\``;
30
+ if (isPG()) return `"${identifier}"`;
31
31
  return identifier; // SQLite 无需引用
32
32
  }
33
33
 
@@ -7,7 +7,7 @@
7
7
  * - 获取表的索引信息
8
8
  */
9
9
 
10
- import { IS_MYSQL, IS_PG, IS_SQLITE } from './constants.js';
10
+ import { isMySQL, isPG, isSQLite } from './constants.js';
11
11
  import type { ColumnInfo, IndexInfo } from '../../types.js';
12
12
  import type { SQL } from 'bun';
13
13
 
@@ -19,22 +19,21 @@ import type { SQL } from 'bun';
19
19
  * @param dbName - 数据库名称
20
20
  * @returns 表是否存在
21
21
  */
22
- export async function tableExists(sql: SQL, tableName: string, dbName?: string): Promise<boolean> {
22
+ export async function tableExists(sql: SQL, tableName: string, dbName: string): Promise<boolean> {
23
23
  if (!sql) throw new Error('SQL 客户端未初始化');
24
- const database = dbName || process.env.DB_NAME;
25
24
 
26
25
  try {
27
- if (IS_MYSQL) {
28
- const res = await sql`SELECT COUNT(*) AS count FROM information_schema.TABLES WHERE TABLE_SCHEMA = ${database} AND TABLE_NAME = ${tableName}`;
26
+ if (isMySQL()) {
27
+ const res = await sql`SELECT COUNT(*) AS count FROM information_schema.TABLES WHERE TABLE_SCHEMA = ${dbName} AND TABLE_NAME = ${tableName}`;
29
28
  return (res[0]?.count || 0) > 0;
30
29
  }
31
30
 
32
- if (IS_PG) {
31
+ if (isPG()) {
33
32
  const res = await sql`SELECT COUNT(*)::int AS count FROM information_schema.tables WHERE table_schema = 'public' AND table_name = ${tableName}`;
34
33
  return (res[0]?.count || 0) > 0;
35
34
  }
36
35
 
37
- if (IS_SQLITE) {
36
+ if (isSQLite()) {
38
37
  const res = await sql`SELECT name FROM sqlite_master WHERE type='table' AND name = ${tableName}`;
39
38
  return res.length > 0;
40
39
  }
@@ -61,16 +60,15 @@ export async function tableExists(sql: SQL, tableName: string, dbName?: string):
61
60
  * @param dbName - 数据库名称
62
61
  * @returns 列信息对象,键为列名,值为列详情
63
62
  */
64
- export async function getTableColumns(sql: SQL, tableName: string, dbName?: string): Promise<{ [key: string]: ColumnInfo }> {
63
+ export async function getTableColumns(sql: SQL, tableName: string, dbName: string): Promise<{ [key: string]: ColumnInfo }> {
65
64
  const columns: { [key: string]: ColumnInfo } = {};
66
- const database = dbName || process.env.DB_NAME;
67
65
 
68
66
  try {
69
- if (IS_MYSQL) {
67
+ if (isMySQL()) {
70
68
  const result = await sql`
71
69
  SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT, COLUMN_TYPE
72
70
  FROM information_schema.COLUMNS
73
- WHERE TABLE_SCHEMA = ${database} AND TABLE_NAME = ${tableName}
71
+ WHERE TABLE_SCHEMA = ${dbName} AND TABLE_NAME = ${tableName}
74
72
  ORDER BY ORDINAL_POSITION
75
73
  `;
76
74
  for (const row of result) {
@@ -91,7 +89,7 @@ export async function getTableColumns(sql: SQL, tableName: string, dbName?: stri
91
89
  comment: row.COLUMN_COMMENT
92
90
  };
93
91
  }
94
- } else if (IS_PG) {
92
+ } else if (isPG()) {
95
93
  const result = await sql`
96
94
  SELECT column_name, data_type, character_maximum_length, is_nullable, column_default
97
95
  FROM information_schema.columns
@@ -119,7 +117,7 @@ export async function getTableColumns(sql: SQL, tableName: string, dbName?: stri
119
117
  comment: commentMap[row.column_name] ?? null
120
118
  };
121
119
  }
122
- } else if (IS_SQLITE) {
120
+ } else if (isSQLite()) {
123
121
  const result = await sql.unsafe(`PRAGMA table_info(${tableName})`);
124
122
  for (const row of result) {
125
123
  let baseType = String(row.type || '').toUpperCase();
@@ -154,16 +152,15 @@ export async function getTableColumns(sql: SQL, tableName: string, dbName?: stri
154
152
  * @param dbName - 数据库名称
155
153
  * @returns 索引信息对象,键为索引名,值为列名数组
156
154
  */
157
- export async function getTableIndexes(sql: SQL, tableName: string, dbName?: string): Promise<IndexInfo> {
155
+ export async function getTableIndexes(sql: SQL, tableName: string, dbName: string): Promise<IndexInfo> {
158
156
  const indexes: IndexInfo = {};
159
- const database = dbName || process.env.DB_NAME;
160
157
 
161
158
  try {
162
- if (IS_MYSQL) {
159
+ if (isMySQL()) {
163
160
  const result = await sql`
164
161
  SELECT INDEX_NAME, COLUMN_NAME
165
162
  FROM information_schema.STATISTICS
166
- WHERE TABLE_SCHEMA = ${database}
163
+ WHERE TABLE_SCHEMA = ${dbName}
167
164
  AND TABLE_NAME = ${tableName}
168
165
  AND INDEX_NAME != 'PRIMARY'
169
166
  ORDER BY INDEX_NAME
@@ -172,7 +169,7 @@ export async function getTableIndexes(sql: SQL, tableName: string, dbName?: stri
172
169
  if (!indexes[row.INDEX_NAME]) indexes[row.INDEX_NAME] = [];
173
170
  indexes[row.INDEX_NAME].push(row.COLUMN_NAME);
174
171
  }
175
- } else if (IS_PG) {
172
+ } else if (isPG()) {
176
173
  const result = await sql`
177
174
  SELECT indexname, indexdef
178
175
  FROM pg_indexes
@@ -185,7 +182,7 @@ export async function getTableIndexes(sql: SQL, tableName: string, dbName?: stri
185
182
  indexes[row.indexname] = [col];
186
183
  }
187
184
  }
188
- } else if (IS_SQLITE) {
185
+ } else if (isSQLite()) {
189
186
  const list = await sql.unsafe(`PRAGMA index_list(${tableName})`);
190
187
  for (const idx of list) {
191
188
  const info = await sql.unsafe(`PRAGMA index_info(${idx.name})`);