befly 3.9.38 → 3.9.40

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 (155) hide show
  1. package/README.md +37 -38
  2. package/befly.config.ts +62 -40
  3. package/checks/checkApi.ts +16 -16
  4. package/checks/checkApp.ts +19 -25
  5. package/checks/checkTable.ts +42 -42
  6. package/docs/README.md +42 -35
  7. package/docs/{api.md → api/api.md} +223 -231
  8. package/docs/cipher.md +71 -69
  9. package/docs/database.md +143 -141
  10. package/docs/{examples.md → guide/examples.md} +181 -181
  11. package/docs/guide/quickstart.md +331 -0
  12. package/docs/hooks/auth.md +38 -0
  13. package/docs/hooks/cors.md +28 -0
  14. package/docs/{hook.md → hooks/hook.md} +140 -57
  15. package/docs/hooks/parser.md +19 -0
  16. package/docs/hooks/rateLimit.md +47 -0
  17. package/docs/{redis.md → infra/redis.md} +84 -93
  18. package/docs/plugins/cipher.md +61 -0
  19. package/docs/plugins/database.md +128 -0
  20. package/docs/{plugin.md → plugins/plugin.md} +83 -81
  21. package/docs/quickstart.md +26 -26
  22. package/docs/{addon.md → reference/addon.md} +46 -46
  23. package/docs/{config.md → reference/config.md} +32 -80
  24. package/docs/{logger.md → reference/logger.md} +52 -52
  25. package/docs/{sync.md → reference/sync.md} +32 -35
  26. package/docs/{table.md → reference/table.md} +1 -1
  27. package/docs/{validator.md → reference/validator.md} +57 -57
  28. package/hooks/auth.ts +8 -4
  29. package/hooks/cors.ts +13 -13
  30. package/hooks/parser.ts +37 -17
  31. package/hooks/permission.ts +26 -14
  32. package/hooks/rateLimit.ts +276 -0
  33. package/hooks/validator.ts +8 -8
  34. package/lib/asyncContext.ts +43 -0
  35. package/lib/cacheHelper.ts +212 -77
  36. package/lib/cacheKeys.ts +38 -0
  37. package/lib/cipher.ts +30 -30
  38. package/lib/connect.ts +28 -28
  39. package/lib/dbHelper.ts +183 -102
  40. package/lib/jwt.ts +16 -16
  41. package/lib/logger.ts +610 -19
  42. package/lib/redisHelper.ts +185 -44
  43. package/lib/sqlBuilder.ts +90 -91
  44. package/lib/validator.ts +59 -39
  45. package/loader/loadApis.ts +48 -44
  46. package/loader/loadHooks.ts +40 -14
  47. package/loader/loadPlugins.ts +16 -17
  48. package/main.ts +57 -47
  49. package/package.json +47 -45
  50. package/paths.ts +15 -14
  51. package/plugins/cache.ts +5 -4
  52. package/plugins/cipher.ts +3 -3
  53. package/plugins/config.ts +2 -2
  54. package/plugins/db.ts +9 -9
  55. package/plugins/jwt.ts +3 -3
  56. package/plugins/logger.ts +8 -12
  57. package/plugins/redis.ts +8 -8
  58. package/plugins/tool.ts +6 -6
  59. package/router/api.ts +85 -56
  60. package/router/static.ts +12 -12
  61. package/sync/syncAll.ts +12 -12
  62. package/sync/syncApi.ts +55 -52
  63. package/sync/syncDb/apply.ts +20 -19
  64. package/sync/syncDb/constants.ts +25 -23
  65. package/sync/syncDb/ddl.ts +35 -36
  66. package/sync/syncDb/helpers.ts +6 -9
  67. package/sync/syncDb/schema.ts +10 -9
  68. package/sync/syncDb/sqlite.ts +7 -8
  69. package/sync/syncDb/table.ts +37 -35
  70. package/sync/syncDb/tableCreate.ts +21 -20
  71. package/sync/syncDb/types.ts +23 -20
  72. package/sync/syncDb/version.ts +10 -10
  73. package/sync/syncDb.ts +43 -36
  74. package/sync/syncDev.ts +74 -65
  75. package/sync/syncMenu.ts +190 -55
  76. package/tests/api-integration-array-number.test.ts +282 -0
  77. package/tests/befly-config-env.test.ts +78 -0
  78. package/tests/cacheHelper.test.ts +135 -104
  79. package/tests/cacheKeys.test.ts +41 -0
  80. package/tests/cipher.test.ts +90 -89
  81. package/tests/dbHelper-advanced.test.ts +140 -134
  82. package/tests/dbHelper-all-array-types.test.ts +316 -0
  83. package/tests/dbHelper-array-serialization.test.ts +258 -0
  84. package/tests/dbHelper-columns.test.ts +56 -55
  85. package/tests/dbHelper-execute.test.ts +45 -44
  86. package/tests/dbHelper-joins.test.ts +124 -119
  87. package/tests/fields-redis-cache.test.ts +29 -27
  88. package/tests/fields-validate.test.ts +38 -38
  89. package/tests/getClientIp.test.ts +54 -0
  90. package/tests/integration.test.ts +69 -67
  91. package/tests/jwt.test.ts +27 -26
  92. package/tests/logger.test.ts +267 -34
  93. package/tests/rateLimit-hook.test.ts +477 -0
  94. package/tests/redisHelper.test.ts +187 -188
  95. package/tests/redisKeys.test.ts +6 -73
  96. package/tests/scanConfig.test.ts +144 -0
  97. package/tests/sqlBuilder-advanced.test.ts +217 -215
  98. package/tests/sqlBuilder.test.ts +92 -91
  99. package/tests/sync-connection.test.ts +29 -29
  100. package/tests/syncDb-apply.test.ts +97 -96
  101. package/tests/syncDb-array-number.test.ts +160 -0
  102. package/tests/syncDb-constants.test.ts +48 -47
  103. package/tests/syncDb-ddl.test.ts +99 -98
  104. package/tests/syncDb-helpers.test.ts +29 -28
  105. package/tests/syncDb-schema.test.ts +61 -60
  106. package/tests/syncDb-types.test.ts +60 -59
  107. package/tests/syncMenu-paths.test.ts +68 -0
  108. package/tests/util.test.ts +42 -41
  109. package/tests/validator-array-number.test.ts +310 -0
  110. package/tests/validator-default.test.ts +373 -0
  111. package/tests/validator.test.ts +271 -266
  112. package/tsconfig.json +4 -5
  113. package/types/api.d.ts +7 -12
  114. package/types/befly.d.ts +60 -13
  115. package/types/cache.d.ts +8 -4
  116. package/types/common.d.ts +17 -9
  117. package/types/context.d.ts +2 -2
  118. package/types/crypto.d.ts +23 -0
  119. package/types/database.d.ts +19 -19
  120. package/types/hook.d.ts +2 -2
  121. package/types/jwt.d.ts +118 -0
  122. package/types/logger.d.ts +30 -0
  123. package/types/plugin.d.ts +4 -4
  124. package/types/redis.d.ts +7 -3
  125. package/types/roleApisCache.ts +23 -0
  126. package/types/sync.d.ts +10 -10
  127. package/types/table.d.ts +50 -9
  128. package/types/validate.d.ts +69 -0
  129. package/utils/addonHelper.ts +90 -0
  130. package/utils/arrayKeysToCamel.ts +18 -0
  131. package/utils/calcPerfTime.ts +13 -0
  132. package/utils/configTypes.ts +3 -0
  133. package/utils/cors.ts +19 -0
  134. package/utils/fieldClear.ts +75 -0
  135. package/utils/genShortId.ts +12 -0
  136. package/utils/getClientIp.ts +45 -0
  137. package/utils/keysToCamel.ts +22 -0
  138. package/utils/keysToSnake.ts +22 -0
  139. package/utils/modules.ts +98 -0
  140. package/utils/pickFields.ts +19 -0
  141. package/utils/process.ts +56 -0
  142. package/utils/regex.ts +225 -0
  143. package/utils/response.ts +115 -0
  144. package/utils/route.ts +23 -0
  145. package/utils/scanConfig.ts +142 -0
  146. package/utils/scanFiles.ts +48 -0
  147. package/.prettierignore +0 -2
  148. package/.prettierrc +0 -12
  149. package/docs/1-/345/237/272/346/234/254/344/273/213/347/273/215.md +0 -35
  150. package/docs/2-/345/210/235/346/255/245/344/275/223/351/252/214.md +0 -64
  151. package/docs/3-/347/254/254/344/270/200/344/270/252/346/216/245/345/217/243.md +0 -46
  152. package/docs/4-/346/223/215/344/275/234/346/225/260/346/215/256/345/272/223.md +0 -172
  153. package/hooks/requestLogger.ts +0 -84
  154. package/types/index.ts +0 -24
  155. package/util.ts +0 -283
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * syncDb 变更应用模块
3
3
  *
4
4
  * 包含:
@@ -6,14 +6,15 @@
6
6
  * - 应用表结构变更计划
7
7
  */
8
8
 
9
- import { Logger } from '../../lib/logger.js';
10
- import { isMySQL, isPG, isSQLite, IS_PLAN, CHANGE_TYPE_LABELS, getTypeMapping } from './constants.js';
11
- import { logFieldChange, resolveDefaultValue, isStringOrArrayType } from './helpers.js';
12
- import { executeDDLSafely, buildIndexSQL } from './ddl.js';
13
- import { rebuildSqliteTable } from './sqlite.js';
14
- import type { FieldChange, IndexAction, TablePlan, ColumnInfo } from '../../types/sync.js';
15
- import type { SQL } from 'bun';
16
- import type { FieldDefinition } from 'befly-shared/types';
9
+ import type { FieldChange, TablePlan, ColumnInfo } from "../../types/sync.js";
10
+ import type { FieldDefinition } from "../../types/validate.js";
11
+ import type { SQL } from "bun";
12
+
13
+ import { Logger } from "../../lib/logger.js";
14
+ import { isMySQL, isPG, isSQLite, IS_PLAN, getTypeMapping } from "./constants.js";
15
+ import { executeDDLSafely, buildIndexSQL } from "./ddl.js";
16
+ import { rebuildSqliteTable } from "./sqlite.js";
17
+ import { isStringOrArrayType, resolveDefaultValue } from "./types.js";
17
18
 
18
19
  /**
19
20
  * 构建 ALTER TABLE SQL 语句
@@ -28,9 +29,9 @@ import type { FieldDefinition } from 'befly-shared/types';
28
29
  */
29
30
  function buildAlterTableSQL(tableName: string, clauses: string[]): string {
30
31
  if (isMySQL()) {
31
- return `ALTER TABLE \`${tableName}\` ${clauses.join(', ')}, ALGORITHM=INSTANT, LOCK=NONE`;
32
+ return `ALTER TABLE \`${tableName}\` ${clauses.join(", ")}, ALGORITHM=INSTANT, LOCK=NONE`;
32
33
  }
33
- return `ALTER TABLE "${tableName}" ${clauses.join(', ')}`;
34
+ return `ALTER TABLE "${tableName}" ${clauses.join(", ")}`;
34
35
  }
35
36
 
36
37
  /**
@@ -54,7 +55,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
54
55
  if (!isSQLite() && isStringOrArrayType(fieldDef.type)) {
55
56
  if (existingColumn.max !== fieldDef.max) {
56
57
  changes.push({
57
- type: 'length',
58
+ type: "length",
58
59
  current: existingColumn.max,
59
60
  expected: fieldDef.max
60
61
  });
@@ -63,10 +64,10 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
63
64
 
64
65
  // 检查注释变化(MySQL/PG 支持列注释,对比数据库 comment 与字段 name)
65
66
  if (!isSQLite()) {
66
- const currentComment = existingColumn.comment || '';
67
+ const currentComment = existingColumn.comment || "";
67
68
  if (currentComment !== fieldDef.name) {
68
69
  changes.push({
69
- type: 'comment',
70
+ type: "comment",
70
71
  current: currentComment,
71
72
  expected: fieldDef.name
72
73
  });
@@ -80,7 +81,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
80
81
 
81
82
  if (currentType !== expectedType) {
82
83
  changes.push({
83
- type: 'datatype',
84
+ type: "datatype",
84
85
  current: currentType,
85
86
  expected: expectedType
86
87
  });
@@ -90,7 +91,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
90
91
  const expectedNullable = fieldDef.nullable;
91
92
  if (existingColumn.nullable !== expectedNullable) {
92
93
  changes.push({
93
- type: 'nullable',
94
+ type: "nullable",
94
95
  current: existingColumn.nullable,
95
96
  expected: expectedNullable
96
97
  });
@@ -102,7 +103,7 @@ export function compareFieldDefinition(existingColumn: ColumnInfo, fieldDef: Fie
102
103
  // 检查默认值变化
103
104
  if (String(existingColumn.defaultValue) !== String(expectedDefault)) {
104
105
  changes.push({
105
- type: 'default',
106
+ type: "default",
106
107
  current: existingColumn.defaultValue,
107
108
  expected: expectedDefault
108
109
  });
@@ -169,13 +170,13 @@ export async function applyTablePlan(sql: SQL, tableName: string, fields: Record
169
170
  } else {
170
171
  try {
171
172
  await sql.unsafe(stmt);
172
- if (act.action === 'create') {
173
+ if (act.action === "create") {
173
174
  Logger.debug(`[索引变化] 新建索引 ${tableName}.${act.indexName} (${act.fieldName})`);
174
175
  } else {
175
176
  Logger.debug(`[索引变化] 删除索引 ${tableName}.${act.indexName} (${act.fieldName})`);
176
177
  }
177
178
  } catch (error: any) {
178
- Logger.error({ err: error, table: tableName, index: act.indexName, field: act.fieldName }, `${act.action === 'create' ? '创建' : '删除'}索引失败`);
179
+ Logger.error({ err: error, table: tableName, index: act.indexName, field: act.fieldName }, `${act.action === "create" ? "创建" : "删除"}索引失败`);
179
180
  throw error;
180
181
  }
181
182
  }
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * syncDb 常量定义模块
3
3
  *
4
4
  * 包含:
@@ -14,25 +14,25 @@
14
14
  export const DB_VERSION_REQUIREMENTS = {
15
15
  MYSQL_MIN_MAJOR: 8,
16
16
  POSTGRES_MIN_MAJOR: 17,
17
- SQLITE_MIN_VERSION: '3.50.0',
17
+ SQLITE_MIN_VERSION: "3.50.0",
18
18
  SQLITE_MIN_VERSION_NUM: 35000 // 3 * 10000 + 50 * 100
19
19
  } as const;
20
20
 
21
21
  /**
22
22
  * 需要创建索引的系统字段
23
23
  */
24
- export const SYSTEM_INDEX_FIELDS = ['created_at', 'updated_at', 'state'] as const;
24
+ export const SYSTEM_INDEX_FIELDS = ["created_at", "updated_at", "state"] as const;
25
25
 
26
26
  /**
27
27
  * 字段变更类型的中文标签映射
28
28
  */
29
29
  export const CHANGE_TYPE_LABELS = {
30
- length: '长度',
31
- datatype: '类型',
32
- comment: '注释',
33
- default: '默认值',
34
- nullable: '可空约束',
35
- unique: '唯一约束'
30
+ length: "长度",
31
+ datatype: "类型",
32
+ comment: "注释",
33
+ default: "默认值",
34
+ nullable: "可空约束",
35
+ unique: "唯一约束"
36
36
  } as const;
37
37
 
38
38
  /**
@@ -44,23 +44,23 @@ export const CHANGE_TYPE_LABELS = {
44
44
  * - COLLATE: utf8mb4_0900_ai_ci(MySQL 8.0 推荐,不区分重音和大小写)
45
45
  */
46
46
  export const MYSQL_TABLE_CONFIG = {
47
- ENGINE: 'InnoDB',
48
- CHARSET: 'utf8mb4',
49
- COLLATE: 'utf8mb4_0900_ai_ci'
47
+ ENGINE: "InnoDB",
48
+ CHARSET: "utf8mb4",
49
+ COLLATE: "utf8mb4_0900_ai_ci"
50
50
  } as const;
51
51
 
52
52
  // 是否为计划模式(仅输出 SQL 不执行)
53
- export const IS_PLAN = process.argv.includes('--plan');
53
+ export const IS_PLAN = process.argv.includes("--plan");
54
54
 
55
55
  // 数据库类型(运行时设置,默认 mysql)
56
- let _dbType: string = 'mysql';
56
+ let _dbType: string = "mysql";
57
57
 
58
58
  /**
59
59
  * 设置数据库类型(由 syncDbCommand 调用)
60
60
  * @param dbType - 数据库类型(mysql/postgresql/postgres/sqlite)
61
61
  */
62
62
  export function setDbType(dbType: string): void {
63
- _dbType = (dbType || 'mysql').toLowerCase();
63
+ _dbType = (dbType || "mysql").toLowerCase();
64
64
  }
65
65
 
66
66
  /**
@@ -72,15 +72,15 @@ export function getDbType(): string {
72
72
 
73
73
  // 数据库类型判断(getter 函数,运行时动态计算)
74
74
  export function isMySQL(): boolean {
75
- return _dbType === 'mysql';
75
+ return _dbType === "mysql";
76
76
  }
77
77
 
78
78
  export function isPG(): boolean {
79
- return _dbType === 'postgresql' || _dbType === 'postgres';
79
+ return _dbType === "postgresql" || _dbType === "postgres";
80
80
  }
81
81
 
82
82
  export function isSQLite(): boolean {
83
- return _dbType === 'sqlite';
83
+ return _dbType === "sqlite";
84
84
  }
85
85
 
86
86
  // 兼容旧代码的静态别名(通过 getter 实现动态获取)
@@ -108,10 +108,12 @@ export function getTypeMapping(): Record<string, string> {
108
108
  const isMysql = isMySQL();
109
109
 
110
110
  return {
111
- number: isSqlite ? 'INTEGER' : isPg ? 'BIGINT' : 'BIGINT',
112
- string: isSqlite ? 'TEXT' : isPg ? 'character varying' : 'VARCHAR',
113
- text: isMysql ? 'MEDIUMTEXT' : 'TEXT',
114
- array_string: isSqlite ? 'TEXT' : isPg ? 'character varying' : 'VARCHAR',
115
- array_text: isMysql ? 'MEDIUMTEXT' : 'TEXT'
111
+ number: isSqlite ? "INTEGER" : isPg ? "BIGINT" : "BIGINT",
112
+ string: isSqlite ? "TEXT" : isPg ? "character varying" : "VARCHAR",
113
+ text: isMysql ? "MEDIUMTEXT" : "TEXT",
114
+ array_string: isSqlite ? "TEXT" : isPg ? "character varying" : "VARCHAR",
115
+ array_text: isMysql ? "MEDIUMTEXT" : "TEXT",
116
+ array_number_string: isSqlite ? "TEXT" : isPg ? "character varying" : "VARCHAR",
117
+ array_number_text: isMysql ? "MEDIUMTEXT" : "TEXT"
116
118
  };
117
119
  }
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * syncDb DDL 构建模块
3
3
  *
4
4
  * 包含:
@@ -8,15 +8,14 @@
8
8
  * - 构建系统列和业务列定义
9
9
  */
10
10
 
11
- import { snakeCase } from 'es-toolkit/string';
12
- import { Logger } from '../../lib/logger.js';
13
- import { isMySQL, isPG, getTypeMapping } from './constants.js';
14
- import { quoteIdentifier, escapeComment } from './helpers.js';
15
- import { resolveDefaultValue, generateDefaultSql, getSqlType } from './types.js';
11
+ import type { FieldDefinition } from "../../types/validate.js";
12
+ import type { SQL } from "bun";
13
+
14
+ import { snakeCase } from "es-toolkit/string";
16
15
 
17
- import type { SQL } from 'bun';
18
- import type { FieldDefinition } from 'befly-shared/types';
19
- import type { AnyObject } from '../../types/common.js';
16
+ import { isMySQL, isPG } from "./constants.js";
17
+ import { quoteIdentifier, escapeComment } from "./helpers.js";
18
+ import { resolveDefaultValue, generateDefaultSql, getSqlType } from "./types.js";
20
19
 
21
20
  /**
22
21
  * 构建索引操作 SQL(统一使用在线策略)
@@ -27,26 +26,26 @@ import type { AnyObject } from '../../types/common.js';
27
26
  * @param action - 操作类型(create/drop)
28
27
  * @returns SQL 语句
29
28
  */
30
- export function buildIndexSQL(tableName: string, indexName: string, fieldName: string, action: 'create' | 'drop'): string {
29
+ export function buildIndexSQL(tableName: string, indexName: string, fieldName: string, action: "create" | "drop"): string {
31
30
  const tableQuoted = quoteIdentifier(tableName);
32
31
  const indexQuoted = quoteIdentifier(indexName);
33
32
  const fieldQuoted = quoteIdentifier(fieldName);
34
33
 
35
34
  if (isMySQL()) {
36
35
  const parts = [];
37
- if (action === 'create') {
36
+ if (action === "create") {
38
37
  parts.push(`ADD INDEX ${indexQuoted} (${fieldQuoted})`);
39
38
  } else {
40
39
  parts.push(`DROP INDEX ${indexQuoted}`);
41
40
  }
42
41
  // 始终使用在线算法
43
- parts.push('ALGORITHM=INPLACE');
44
- parts.push('LOCK=NONE');
45
- return `ALTER TABLE ${tableQuoted} ${parts.join(', ')}`;
42
+ parts.push("ALGORITHM=INPLACE");
43
+ parts.push("LOCK=NONE");
44
+ return `ALTER TABLE ${tableQuoted} ${parts.join(", ")}`;
46
45
  }
47
46
 
48
47
  if (isPG()) {
49
- if (action === 'create') {
48
+ if (action === "create") {
50
49
  // 始终使用 CONCURRENTLY
51
50
  return `CREATE INDEX CONCURRENTLY IF NOT EXISTS ${indexQuoted} ON ${tableQuoted}(${fieldQuoted})`;
52
51
  }
@@ -54,7 +53,7 @@ export function buildIndexSQL(tableName: string, indexName: string, fieldName: s
54
53
  }
55
54
 
56
55
  // SQLite
57
- if (action === 'create') {
56
+ if (action === "create") {
58
57
  return `CREATE INDEX IF NOT EXISTS ${indexQuoted} ON ${tableQuoted}(${fieldQuoted})`;
59
58
  }
60
59
  return `DROP INDEX IF EXISTS ${indexQuoted}`;
@@ -92,7 +91,7 @@ export function getSystemColumnDef(fieldName: string): string | null {
92
91
  * @returns 系统字段的列定义数组
93
92
  */
94
93
  export function buildSystemColumnDefs(): string[] {
95
- return [getSystemColumnDef('id')!, getSystemColumnDef('created_at')!, getSystemColumnDef('updated_at')!, getSystemColumnDef('deleted_at')!, getSystemColumnDef('state')!];
94
+ return [getSystemColumnDef("id")!, getSystemColumnDef("created_at")!, getSystemColumnDef("updated_at")!, getSystemColumnDef("deleted_at")!, getSystemColumnDef("state")!];
96
95
  }
97
96
 
98
97
  /**
@@ -115,8 +114,8 @@ export function buildBusinessColumnDefs(fields: Record<string, FieldDefinition>)
115
114
  const defaultSql = generateDefaultSql(actualDefault, fieldDef.type);
116
115
 
117
116
  // 构建约束
118
- const uniqueSql = fieldDef.unique ? ' UNIQUE' : '';
119
- const nullableSql = fieldDef.nullable ? ' NULL' : ' NOT NULL';
117
+ const uniqueSql = fieldDef.unique ? " UNIQUE" : "";
118
+ const nullableSql = fieldDef.nullable ? " NULL" : " NOT NULL";
120
119
 
121
120
  if (isMySQL()) {
122
121
  colDefs.push(`\`${dbFieldName}\` ${sqlType}${uniqueSql}${nullableSql}${defaultSql} COMMENT "${escapeComment(fieldDef.name)}"`);
@@ -147,11 +146,11 @@ export function generateDDLClause(fieldKey: string, fieldDef: FieldDefinition, i
147
146
  const defaultSql = generateDefaultSql(actualDefault, fieldDef.type);
148
147
 
149
148
  // 构建约束
150
- const uniqueSql = fieldDef.unique ? ' UNIQUE' : '';
151
- const nullableSql = fieldDef.nullable ? ' NULL' : ' NOT NULL';
149
+ const uniqueSql = fieldDef.unique ? " UNIQUE" : "";
150
+ const nullableSql = fieldDef.nullable ? " NULL" : " NOT NULL";
152
151
 
153
152
  if (isMySQL()) {
154
- return `${isAdd ? 'ADD COLUMN' : 'MODIFY COLUMN'} \`${dbFieldName}\` ${sqlType}${uniqueSql}${nullableSql}${defaultSql} COMMENT "${escapeComment(fieldDef.name)}"`;
153
+ return `${isAdd ? "ADD COLUMN" : "MODIFY COLUMN"} \`${dbFieldName}\` ${sqlType}${uniqueSql}${nullableSql}${defaultSql} COMMENT "${escapeComment(fieldDef.name)}"`;
155
154
  }
156
155
  if (isPG()) {
157
156
  if (isAdd) return `ADD COLUMN IF NOT EXISTS "${dbFieldName}" ${sqlType}${uniqueSql}${nullableSql}${defaultSql}`;
@@ -160,7 +159,7 @@ export function generateDDLClause(fieldKey: string, fieldDef: FieldDefinition, i
160
159
  }
161
160
  // SQLite 仅支持 ADD COLUMN(>=3.50.0:支持 IF NOT EXISTS)
162
161
  if (isAdd) return `ADD COLUMN IF NOT EXISTS "${dbFieldName}" ${sqlType}${uniqueSql}${nullableSql}${defaultSql}`;
163
- return '';
162
+ return "";
164
163
  }
165
164
 
166
165
  /**
@@ -182,17 +181,17 @@ export async function executeDDLSafely(sql: SQL, stmt: string): Promise<boolean>
182
181
  return true;
183
182
  } catch (error: any) {
184
183
  // MySQL 专用降级路径
185
- if (stmt.includes('ALGORITHM=INSTANT')) {
186
- const inplaceSql = stmt.replace(/ALGORITHM=INSTANT/g, 'ALGORITHM=INPLACE');
184
+ if (stmt.includes("ALGORITHM=INSTANT")) {
185
+ const inplaceSql = stmt.replace(/ALGORITHM=INSTANT/g, "ALGORITHM=INPLACE");
187
186
  try {
188
187
  await sql.unsafe(inplaceSql);
189
188
  return true;
190
- } catch (inplaceError) {
189
+ } catch {
191
190
  // 最后尝试传统DDL:移除 ALGORITHM/LOCK 附加子句后执行
192
191
  const traditionSql = stmt
193
- .replace(/,\s*ALGORITHM=INPLACE/g, '')
194
- .replace(/,\s*ALGORITHM=INSTANT/g, '')
195
- .replace(/,\s*LOCK=(NONE|SHARED|EXCLUSIVE)/g, '');
192
+ .replace(/,\s*ALGORITHM=INPLACE/g, "")
193
+ .replace(/,\s*ALGORITHM=INSTANT/g, "")
194
+ .replace(/,\s*LOCK=(NONE|SHARED|EXCLUSIVE)/g, "");
196
195
  await sql.unsafe(traditionSql);
197
196
  return true;
198
197
  }
@@ -216,8 +215,8 @@ export async function executeDDLSafely(sql: SQL, stmt: string): Promise<boolean>
216
215
  * @returns 是否为兼容变更
217
216
  */
218
217
  export function isCompatibleTypeChange(currentType: string, newType: string): boolean {
219
- const c = String(currentType || '').toLowerCase();
220
- const n = String(newType || '').toLowerCase();
218
+ const c = String(currentType || "").toLowerCase();
219
+ const n = String(newType || "").toLowerCase();
221
220
 
222
221
  // 相同类型不算变更
223
222
  if (c === n) return false;
@@ -226,8 +225,8 @@ export function isCompatibleTypeChange(currentType: string, newType: string): bo
226
225
  const extractBaseType = (t: string): string => {
227
226
  // 移除 unsigned 和括号内容
228
227
  return t
229
- .replace(/\s*unsigned/gi, '')
230
- .replace(/\([^)]*\)/g, '')
228
+ .replace(/\s*unsigned/gi, "")
229
+ .replace(/\([^)]*\)/g, "")
231
230
  .trim();
232
231
  };
233
232
 
@@ -235,7 +234,7 @@ export function isCompatibleTypeChange(currentType: string, newType: string): bo
235
234
  const nBase = extractBaseType(n);
236
235
 
237
236
  // MySQL/通用 整数类型宽化(小 -> 大)
238
- const intTypes = ['tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint'];
237
+ const intTypes = ["tinyint", "smallint", "mediumint", "int", "integer", "bigint"];
239
238
  const cIntIdx = intTypes.indexOf(cBase);
240
239
  const nIntIdx = intTypes.indexOf(nBase);
241
240
  if (cIntIdx !== -1 && nIntIdx !== -1 && nIntIdx > cIntIdx) {
@@ -244,9 +243,9 @@ export function isCompatibleTypeChange(currentType: string, newType: string): bo
244
243
 
245
244
  // 字符串类型宽化
246
245
  // MySQL: varchar -> text/mediumtext/longtext
247
- if (cBase === 'varchar' && (nBase === 'text' || nBase === 'mediumtext' || nBase === 'longtext')) return true;
246
+ if (cBase === "varchar" && (nBase === "text" || nBase === "mediumtext" || nBase === "longtext")) return true;
248
247
  // PG: character varying -> text
249
- if (cBase === 'character varying' && nBase === 'text') return true;
248
+ if (cBase === "character varying" && nBase === "text") return true;
250
249
 
251
250
  return false;
252
251
  }
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * syncDb 辅助工具模块
3
3
  *
4
4
  * 包含:
@@ -7,11 +7,8 @@
7
7
  * - 字段默认值应用
8
8
  */
9
9
 
10
- import { isMySQL, isPG } from './constants.js';
11
- import { Logger } from '../../lib/logger.js';
12
-
13
- // 从 types.ts 重新导出,保持向后兼容
14
- export { isStringOrArrayType, getSqlType, resolveDefaultValue, generateDefaultSql } from './types.js';
10
+ import { Logger } from "../../lib/logger.js";
11
+ import { isMySQL, isPG } from "./constants.js";
15
12
 
16
13
  /**
17
14
  * 根据数据库类型引用标识符
@@ -66,7 +63,7 @@ export function logFieldChange(tableName: string, fieldName: string, changeType:
66
63
  * @returns 格式化的字符串(逗号分隔)
67
64
  */
68
65
  export function formatFieldList(fields: string[]): string {
69
- return fields.map((f) => quoteIdentifier(f)).join(', ');
66
+ return fields.map((f) => quoteIdentifier(f)).join(", ");
70
67
  }
71
68
 
72
69
  /**
@@ -75,9 +72,9 @@ export function formatFieldList(fields: string[]): string {
75
72
  * @param fieldDef - 字段定义对象
76
73
  */
77
74
  export function applyFieldDefaults(fieldDef: any): void {
78
- fieldDef.detail = fieldDef.detail ?? '';
75
+ fieldDef.detail = fieldDef.detail ?? "";
79
76
  fieldDef.min = fieldDef.min ?? 0;
80
- fieldDef.max = fieldDef.max ?? (fieldDef.type === 'number' ? 9999999999999999 : 100);
77
+ fieldDef.max = fieldDef.max ?? (fieldDef.type === "number" ? Number.MAX_SAFE_INTEGER : 100);
81
78
  fieldDef.default = fieldDef.default ?? null;
82
79
  fieldDef.index = fieldDef.index ?? false;
83
80
  fieldDef.unique = fieldDef.unique ?? false;
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * syncDb 表结构查询模块
3
3
  *
4
4
  * 包含:
@@ -7,9 +7,10 @@
7
7
  * - 获取表的索引信息
8
8
  */
9
9
 
10
- import { isMySQL, isPG, isSQLite } from './constants.js';
11
- import type { ColumnInfo, IndexInfo } from '../../types/sync.js';
12
- import type { SQL } from 'bun';
10
+ import type { ColumnInfo, IndexInfo } from "../../types/sync.js";
11
+ import type { SQL } from "bun";
12
+
13
+ import { isMySQL, isPG, isSQLite } from "./constants.js";
13
14
 
14
15
  /**
15
16
  * 判断表是否存在(返回布尔值)
@@ -20,7 +21,7 @@ import type { SQL } from 'bun';
20
21
  * @returns 表是否存在
21
22
  */
22
23
  export async function tableExists(sql: SQL, tableName: string, dbName: string): Promise<boolean> {
23
- if (!sql) throw new Error('SQL 客户端未初始化');
24
+ if (!sql) throw new Error("SQL 客户端未初始化");
24
25
 
25
26
  try {
26
27
  if (isMySQL()) {
@@ -85,7 +86,7 @@ export async function getTableColumns(sql: SQL, tableName: string, dbName: strin
85
86
  columnType: row.COLUMN_TYPE,
86
87
  length: row.CHARACTER_MAXIMUM_LENGTH,
87
88
  max: row.CHARACTER_MAXIMUM_LENGTH,
88
- nullable: row.IS_NULLABLE === 'YES',
89
+ nullable: row.IS_NULLABLE === "YES",
89
90
  defaultValue: defaultValue,
90
91
  comment: row.COLUMN_COMMENT
91
92
  };
@@ -114,7 +115,7 @@ export async function getTableColumns(sql: SQL, tableName: string, dbName: strin
114
115
  columnType: row.data_type,
115
116
  length: row.character_maximum_length,
116
117
  max: row.character_maximum_length,
117
- nullable: String(row.is_nullable).toUpperCase() === 'YES',
118
+ nullable: String(row.is_nullable).toUpperCase() === "YES",
118
119
  defaultValue: row.column_default,
119
120
  comment: commentMap[row.column_name] ?? null
120
121
  };
@@ -122,7 +123,7 @@ export async function getTableColumns(sql: SQL, tableName: string, dbName: strin
122
123
  } else if (isSQLite()) {
123
124
  const result = await sql.unsafe(`PRAGMA table_info(${tableName})`);
124
125
  for (const row of result) {
125
- let baseType = String(row.type || '').toUpperCase();
126
+ let baseType = String(row.type || "").toUpperCase();
126
127
  let max = null;
127
128
  const m = /^(\w+)\s*\((\d+)\)/.exec(baseType);
128
129
  if (m) {
@@ -181,7 +182,7 @@ export async function getTableIndexes(sql: SQL, tableName: string, dbName: strin
181
182
  for (const row of result) {
182
183
  const m = /\(([^)]+)\)/.exec(row.indexdef);
183
184
  if (m) {
184
- const col = m[1].replace(/\"/g, '').replace(/"/g, '').trim();
185
+ const col = m[1].replace(/"/g, "").trim();
185
186
  indexes[row.indexname] = [col];
186
187
  }
187
188
  }
@@ -1,15 +1,14 @@
1
- /**
1
+ /**
2
2
  * syncDb SQLite 特殊处理模块
3
3
  *
4
4
  * 包含:
5
5
  * - SQLite 重建表迁移(处理列修改等不支持的操作)
6
6
  */
7
7
 
8
- import { Logger } from '../../lib/logger.js';
9
- import { IS_PLAN } from './constants.js';
10
- import { createTable } from './tableCreate.js';
11
- import type { SQL } from 'bun';
12
- import type { FieldDefinition } from 'befly-shared/types';
8
+ import type { FieldDefinition } from "../../types/validate.js";
9
+ import type { SQL } from "bun";
10
+
11
+ import { createTable } from "./tableCreate.js";
13
12
 
14
13
  /**
15
14
  * SQLite 重建表迁移(简化版)
@@ -30,7 +29,7 @@ export async function rebuildSqliteTable(sql: SQL, tableName: string, fields: Re
30
29
  // 1. 读取现有列顺序
31
30
  const info = await sql.unsafe(`PRAGMA table_info(${tableName})`);
32
31
  const existingCols = info.map((r: any) => r.name);
33
- const targetCols = ['id', 'created_at', 'updated_at', 'deleted_at', 'state', ...Object.keys(fields)];
32
+ const targetCols = ["id", "created_at", "updated_at", "deleted_at", "state", ...Object.keys(fields)];
34
33
  const tmpTable = `${tableName}__tmp__${Date.now()}`;
35
34
 
36
35
  // 2. 创建新表(使用当前定义)
@@ -39,7 +38,7 @@ export async function rebuildSqliteTable(sql: SQL, tableName: string, fields: Re
39
38
  // 3. 拷贝数据(按交集列)
40
39
  const commonCols = targetCols.filter((c) => existingCols.includes(c));
41
40
  if (commonCols.length > 0) {
42
- const colsSql = commonCols.map((c) => `"${c}"`).join(', ');
41
+ const colsSql = commonCols.map((c) => `"${c}"`).join(", ");
43
42
  await sql.unsafe(`INSERT INTO "${tmpTable}" (${colsSql}) SELECT ${colsSql} FROM "${tableName}"`);
44
43
  }
45
44