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
@@ -10,120 +10,121 @@
10
10
  * - typeMapping
11
11
  */
12
12
 
13
- import { describe, test, expect, beforeAll } from 'bun:test';
14
- import { setDbType } from '../sync/syncDb/constants.js';
13
+ import { describe, test, expect, beforeAll } from "bun:test";
14
+
15
+ import { setDbType } from "../sync/syncDb/constants.js";
15
16
 
16
17
  // 设置数据库类型为 MySQL
17
- setDbType('mysql');
18
+ setDbType("mysql");
18
19
 
19
20
  let constants: any;
20
21
 
21
22
  beforeAll(async () => {
22
- constants = await import('../sync/syncDb/constants.js');
23
+ constants = await import("../sync/syncDb/constants.js");
23
24
  });
24
25
 
25
- describe('DB_VERSION_REQUIREMENTS', () => {
26
- test('MySQL 最低版本为 8', () => {
26
+ describe("DB_VERSION_REQUIREMENTS", () => {
27
+ test("MySQL 最低版本为 8", () => {
27
28
  expect(constants.DB_VERSION_REQUIREMENTS.MYSQL_MIN_MAJOR).toBe(8);
28
29
  });
29
30
 
30
- test('PostgreSQL 最低版本为 17', () => {
31
+ test("PostgreSQL 最低版本为 17", () => {
31
32
  expect(constants.DB_VERSION_REQUIREMENTS.POSTGRES_MIN_MAJOR).toBe(17);
32
33
  });
33
34
 
34
- test('SQLite 最低版本为 3.50.0', () => {
35
- expect(constants.DB_VERSION_REQUIREMENTS.SQLITE_MIN_VERSION).toBe('3.50.0');
35
+ test("SQLite 最低版本为 3.50.0", () => {
36
+ expect(constants.DB_VERSION_REQUIREMENTS.SQLITE_MIN_VERSION).toBe("3.50.0");
36
37
  });
37
38
  });
38
39
 
39
- describe('SYSTEM_INDEX_FIELDS', () => {
40
- test('包含 created_at', () => {
41
- expect(constants.SYSTEM_INDEX_FIELDS).toContain('created_at');
40
+ describe("SYSTEM_INDEX_FIELDS", () => {
41
+ test("包含 created_at", () => {
42
+ expect(constants.SYSTEM_INDEX_FIELDS).toContain("created_at");
42
43
  });
43
44
 
44
- test('包含 updated_at', () => {
45
- expect(constants.SYSTEM_INDEX_FIELDS).toContain('updated_at');
45
+ test("包含 updated_at", () => {
46
+ expect(constants.SYSTEM_INDEX_FIELDS).toContain("updated_at");
46
47
  });
47
48
 
48
- test('包含 state', () => {
49
- expect(constants.SYSTEM_INDEX_FIELDS).toContain('state');
49
+ test("包含 state", () => {
50
+ expect(constants.SYSTEM_INDEX_FIELDS).toContain("state");
50
51
  });
51
52
 
52
- test('共 3 个系统索引字段', () => {
53
+ test("共 3 个系统索引字段", () => {
53
54
  expect(constants.SYSTEM_INDEX_FIELDS.length).toBe(3);
54
55
  });
55
56
  });
56
57
 
57
- describe('CHANGE_TYPE_LABELS', () => {
58
+ describe("CHANGE_TYPE_LABELS", () => {
58
59
  test('length 对应 "长度"', () => {
59
- expect(constants.CHANGE_TYPE_LABELS.length).toBe('长度');
60
+ expect(constants.CHANGE_TYPE_LABELS.length).toBe("长度");
60
61
  });
61
62
 
62
63
  test('datatype 对应 "类型"', () => {
63
- expect(constants.CHANGE_TYPE_LABELS.datatype).toBe('类型');
64
+ expect(constants.CHANGE_TYPE_LABELS.datatype).toBe("类型");
64
65
  });
65
66
 
66
67
  test('comment 对应 "注释"', () => {
67
- expect(constants.CHANGE_TYPE_LABELS.comment).toBe('注释');
68
+ expect(constants.CHANGE_TYPE_LABELS.comment).toBe("注释");
68
69
  });
69
70
 
70
71
  test('default 对应 "默认值"', () => {
71
- expect(constants.CHANGE_TYPE_LABELS.default).toBe('默认值');
72
+ expect(constants.CHANGE_TYPE_LABELS.default).toBe("默认值");
72
73
  });
73
74
  });
74
75
 
75
- describe('MYSQL_TABLE_CONFIG', () => {
76
- test('ENGINE 为 InnoDB', () => {
77
- expect(constants.MYSQL_TABLE_CONFIG.ENGINE).toBe('InnoDB');
76
+ describe("MYSQL_TABLE_CONFIG", () => {
77
+ test("ENGINE 为 InnoDB", () => {
78
+ expect(constants.MYSQL_TABLE_CONFIG.ENGINE).toBe("InnoDB");
78
79
  });
79
80
 
80
- test('CHARSET 为 utf8mb4', () => {
81
- expect(constants.MYSQL_TABLE_CONFIG.CHARSET).toBe('utf8mb4');
81
+ test("CHARSET 为 utf8mb4", () => {
82
+ expect(constants.MYSQL_TABLE_CONFIG.CHARSET).toBe("utf8mb4");
82
83
  });
83
84
 
84
- test('COLLATE 为 utf8mb4_0900_ai_ci', () => {
85
- expect(constants.MYSQL_TABLE_CONFIG.COLLATE).toBe('utf8mb4_0900_ai_ci');
85
+ test("COLLATE 为 utf8mb4_0900_ai_ci", () => {
86
+ expect(constants.MYSQL_TABLE_CONFIG.COLLATE).toBe("utf8mb4_0900_ai_ci");
86
87
  });
87
88
  });
88
89
 
89
- describe('getTypeMapping (MySQL)', () => {
90
- test('number 映射为 BIGINT', () => {
91
- expect(constants.getTypeMapping().number).toBe('BIGINT');
90
+ describe("getTypeMapping (MySQL)", () => {
91
+ test("number 映射为 BIGINT", () => {
92
+ expect(constants.getTypeMapping().number).toBe("BIGINT");
92
93
  });
93
94
 
94
- test('string 映射为 VARCHAR', () => {
95
- expect(constants.getTypeMapping().string).toBe('VARCHAR');
95
+ test("string 映射为 VARCHAR", () => {
96
+ expect(constants.getTypeMapping().string).toBe("VARCHAR");
96
97
  });
97
98
 
98
- test('text 映射为 MEDIUMTEXT', () => {
99
- expect(constants.getTypeMapping().text).toBe('MEDIUMTEXT');
99
+ test("text 映射为 MEDIUMTEXT", () => {
100
+ expect(constants.getTypeMapping().text).toBe("MEDIUMTEXT");
100
101
  });
101
102
 
102
- test('array_string 映射为 VARCHAR', () => {
103
- expect(constants.getTypeMapping().array_string).toBe('VARCHAR');
103
+ test("array_string 映射为 VARCHAR", () => {
104
+ expect(constants.getTypeMapping().array_string).toBe("VARCHAR");
104
105
  });
105
106
 
106
- test('array_text 映射为 MEDIUMTEXT', () => {
107
- expect(constants.getTypeMapping().array_text).toBe('MEDIUMTEXT');
107
+ test("array_text 映射为 MEDIUMTEXT", () => {
108
+ expect(constants.getTypeMapping().array_text).toBe("MEDIUMTEXT");
108
109
  });
109
110
  });
110
111
 
111
- describe('IS_PLAN', () => {
112
- test('IS_PLAN 为 boolean 类型', () => {
113
- expect(typeof constants.IS_PLAN).toBe('boolean');
112
+ describe("IS_PLAN", () => {
113
+ test("IS_PLAN 为 boolean 类型", () => {
114
+ expect(typeof constants.IS_PLAN).toBe("boolean");
114
115
  });
115
116
  });
116
117
 
117
- describe('数据库类型判断 (MySQL)', () => {
118
- test('isMySQL 为 true', () => {
118
+ describe("数据库类型判断 (MySQL)", () => {
119
+ test("isMySQL 为 true", () => {
119
120
  expect(constants.isMySQL()).toBe(true);
120
121
  });
121
122
 
122
- test('isPG 为 false', () => {
123
+ test("isPG 为 false", () => {
123
124
  expect(constants.isPG()).toBe(false);
124
125
  });
125
126
 
126
- test('isSQLite 为 false', () => {
127
+ test("isSQLite 为 false", () => {
127
128
  expect(constants.isSQLite()).toBe(false);
128
129
  });
129
130
  });
@@ -9,11 +9,12 @@
9
9
  * - isCompatibleTypeChange
10
10
  */
11
11
 
12
- import { describe, test, expect, beforeAll } from 'bun:test';
13
- import { setDbType } from '../sync/syncDb/constants.js';
12
+ import { describe, test, expect, beforeAll } from "bun:test";
13
+
14
+ import { setDbType } from "../sync/syncDb/constants.js";
14
15
 
15
16
  // 设置数据库类型为 MySQL
16
- setDbType('mysql');
17
+ setDbType("mysql");
17
18
 
18
19
  let buildIndexSQL: any;
19
20
  let buildSystemColumnDefs: any;
@@ -22,7 +23,7 @@ let generateDDLClause: any;
22
23
  let isCompatibleTypeChange: any;
23
24
 
24
25
  beforeAll(async () => {
25
- const ddl = await import('../sync/syncDb/ddl.js');
26
+ const ddl = await import("../sync/syncDb/ddl.js");
26
27
  buildIndexSQL = ddl.buildIndexSQL;
27
28
  buildSystemColumnDefs = ddl.buildSystemColumnDefs;
28
29
  buildBusinessColumnDefs = ddl.buildBusinessColumnDefs;
@@ -30,60 +31,60 @@ beforeAll(async () => {
30
31
  isCompatibleTypeChange = ddl.isCompatibleTypeChange;
31
32
  });
32
33
 
33
- describe('buildIndexSQL (MySQL)', () => {
34
- test('创建索引 SQL', () => {
35
- const sql = buildIndexSQL('user', 'idx_created_at', 'created_at', 'create');
36
- expect(sql).toContain('ALTER TABLE `user`');
37
- expect(sql).toContain('ADD INDEX `idx_created_at`');
38
- expect(sql).toContain('(`created_at`)');
39
- expect(sql).toContain('ALGORITHM=INPLACE');
40
- expect(sql).toContain('LOCK=NONE');
34
+ describe("buildIndexSQL (MySQL)", () => {
35
+ test("创建索引 SQL", () => {
36
+ const sql = buildIndexSQL("user", "idx_created_at", "created_at", "create");
37
+ expect(sql).toContain("ALTER TABLE `user`");
38
+ expect(sql).toContain("ADD INDEX `idx_created_at`");
39
+ expect(sql).toContain("(`created_at`)");
40
+ expect(sql).toContain("ALGORITHM=INPLACE");
41
+ expect(sql).toContain("LOCK=NONE");
41
42
  });
42
43
 
43
- test('删除索引 SQL', () => {
44
- const sql = buildIndexSQL('user', 'idx_created_at', 'created_at', 'drop');
45
- expect(sql).toContain('ALTER TABLE `user`');
46
- expect(sql).toContain('DROP INDEX `idx_created_at`');
44
+ test("删除索引 SQL", () => {
45
+ const sql = buildIndexSQL("user", "idx_created_at", "created_at", "drop");
46
+ expect(sql).toContain("ALTER TABLE `user`");
47
+ expect(sql).toContain("DROP INDEX `idx_created_at`");
47
48
  });
48
49
  });
49
50
 
50
- describe('buildSystemColumnDefs (MySQL)', () => {
51
- test('返回 5 个系统字段定义', () => {
51
+ describe("buildSystemColumnDefs (MySQL)", () => {
52
+ test("返回 5 个系统字段定义", () => {
52
53
  const defs = buildSystemColumnDefs();
53
54
  expect(defs.length).toBe(5);
54
55
  });
55
56
 
56
- test('包含 id 主键', () => {
57
+ test("包含 id 主键", () => {
57
58
  const defs = buildSystemColumnDefs();
58
- const idDef = defs.find((d: string) => d.includes('`id`'));
59
- expect(idDef).toContain('PRIMARY KEY');
60
- expect(idDef).toContain('AUTO_INCREMENT');
61
- expect(idDef).toContain('BIGINT UNSIGNED');
59
+ const idDef = defs.find((d: string) => d.includes("`id`"));
60
+ expect(idDef).toContain("PRIMARY KEY");
61
+ expect(idDef).toContain("AUTO_INCREMENT");
62
+ expect(idDef).toContain("BIGINT UNSIGNED");
62
63
  });
63
64
 
64
- test('包含 created_at 字段', () => {
65
+ test("包含 created_at 字段", () => {
65
66
  const defs = buildSystemColumnDefs();
66
- const def = defs.find((d: string) => d.includes('`created_at`'));
67
- expect(def).toContain('BIGINT UNSIGNED');
68
- expect(def).toContain('NOT NULL');
69
- expect(def).toContain('DEFAULT 0');
67
+ const def = defs.find((d: string) => d.includes("`created_at`"));
68
+ expect(def).toContain("BIGINT UNSIGNED");
69
+ expect(def).toContain("NOT NULL");
70
+ expect(def).toContain("DEFAULT 0");
70
71
  });
71
72
 
72
- test('包含 state 字段', () => {
73
+ test("包含 state 字段", () => {
73
74
  const defs = buildSystemColumnDefs();
74
- const def = defs.find((d: string) => d.includes('`state`'));
75
- expect(def).toContain('BIGINT UNSIGNED');
76
- expect(def).toContain('NOT NULL');
77
- expect(def).toContain('DEFAULT 1');
75
+ const def = defs.find((d: string) => d.includes("`state`"));
76
+ expect(def).toContain("BIGINT UNSIGNED");
77
+ expect(def).toContain("NOT NULL");
78
+ expect(def).toContain("DEFAULT 1");
78
79
  });
79
80
  });
80
81
 
81
- describe('buildBusinessColumnDefs (MySQL)', () => {
82
- test('生成 string 类型字段', () => {
82
+ describe("buildBusinessColumnDefs (MySQL)", () => {
83
+ test("生成 string 类型字段", () => {
83
84
  const fields = {
84
85
  userName: {
85
- name: '用户名',
86
- type: 'string',
86
+ name: "用户名",
87
+ type: "string",
87
88
  max: 50,
88
89
  default: null,
89
90
  unique: false,
@@ -93,18 +94,18 @@ describe('buildBusinessColumnDefs (MySQL)', () => {
93
94
  };
94
95
  const defs = buildBusinessColumnDefs(fields);
95
96
  expect(defs.length).toBe(1);
96
- expect(defs[0]).toContain('`user_name`');
97
- expect(defs[0]).toContain('VARCHAR(50)');
98
- expect(defs[0]).toContain('NOT NULL');
97
+ expect(defs[0]).toContain("`user_name`");
98
+ expect(defs[0]).toContain("VARCHAR(50)");
99
+ expect(defs[0]).toContain("NOT NULL");
99
100
  expect(defs[0]).toContain("DEFAULT ''");
100
101
  expect(defs[0]).toContain('COMMENT "用户名"');
101
102
  });
102
103
 
103
- test('生成 number 类型字段', () => {
104
+ test("生成 number 类型字段", () => {
104
105
  const fields = {
105
106
  age: {
106
- name: '年龄',
107
- type: 'number',
107
+ name: "年龄",
108
+ type: "number",
108
109
  max: null,
109
110
  default: 0,
110
111
  unique: false,
@@ -113,16 +114,16 @@ describe('buildBusinessColumnDefs (MySQL)', () => {
113
114
  }
114
115
  };
115
116
  const defs = buildBusinessColumnDefs(fields);
116
- expect(defs[0]).toContain('`age`');
117
- expect(defs[0]).toContain('BIGINT UNSIGNED');
118
- expect(defs[0]).toContain('DEFAULT 0');
117
+ expect(defs[0]).toContain("`age`");
118
+ expect(defs[0]).toContain("BIGINT UNSIGNED");
119
+ expect(defs[0]).toContain("DEFAULT 0");
119
120
  });
120
121
 
121
- test('生成 unique 字段', () => {
122
+ test("生成 unique 字段", () => {
122
123
  const fields = {
123
124
  email: {
124
- name: '邮箱',
125
- type: 'string',
125
+ name: "邮箱",
126
+ type: "string",
126
127
  max: 100,
127
128
  default: null,
128
129
  unique: true,
@@ -131,14 +132,14 @@ describe('buildBusinessColumnDefs (MySQL)', () => {
131
132
  }
132
133
  };
133
134
  const defs = buildBusinessColumnDefs(fields);
134
- expect(defs[0]).toContain('UNIQUE');
135
+ expect(defs[0]).toContain("UNIQUE");
135
136
  });
136
137
 
137
- test('生成 nullable 字段', () => {
138
+ test("生成 nullable 字段", () => {
138
139
  const fields = {
139
140
  remark: {
140
- name: '备注',
141
- type: 'string',
141
+ name: "备注",
142
+ type: "string",
142
143
  max: 200,
143
144
  default: null,
144
145
  unique: false,
@@ -147,84 +148,84 @@ describe('buildBusinessColumnDefs (MySQL)', () => {
147
148
  }
148
149
  };
149
150
  const defs = buildBusinessColumnDefs(fields);
150
- expect(defs[0]).toContain('NULL');
151
- expect(defs[0]).not.toContain('NOT NULL');
151
+ expect(defs[0]).toContain("NULL");
152
+ expect(defs[0]).not.toContain("NOT NULL");
152
153
  });
153
154
  });
154
155
 
155
- describe('generateDDLClause (MySQL)', () => {
156
- test('生成 ADD COLUMN 子句', () => {
156
+ describe("generateDDLClause (MySQL)", () => {
157
+ test("生成 ADD COLUMN 子句", () => {
157
158
  const fieldDef = {
158
- name: '用户名',
159
- type: 'string',
159
+ name: "用户名",
160
+ type: "string",
160
161
  max: 50,
161
162
  default: null,
162
163
  unique: false,
163
164
  nullable: false,
164
165
  unsigned: true
165
166
  };
166
- const clause = generateDDLClause('userName', fieldDef, true);
167
- expect(clause).toContain('ADD COLUMN');
168
- expect(clause).toContain('`user_name`');
169
- expect(clause).toContain('VARCHAR(50)');
167
+ const clause = generateDDLClause("userName", fieldDef, true);
168
+ expect(clause).toContain("ADD COLUMN");
169
+ expect(clause).toContain("`user_name`");
170
+ expect(clause).toContain("VARCHAR(50)");
170
171
  });
171
172
 
172
- test('生成 MODIFY COLUMN 子句', () => {
173
+ test("生成 MODIFY COLUMN 子句", () => {
173
174
  const fieldDef = {
174
- name: '用户名',
175
- type: 'string',
175
+ name: "用户名",
176
+ type: "string",
176
177
  max: 100,
177
178
  default: null,
178
179
  unique: false,
179
180
  nullable: false,
180
181
  unsigned: true
181
182
  };
182
- const clause = generateDDLClause('userName', fieldDef, false);
183
- expect(clause).toContain('MODIFY COLUMN');
184
- expect(clause).toContain('`user_name`');
185
- expect(clause).toContain('VARCHAR(100)');
183
+ const clause = generateDDLClause("userName", fieldDef, false);
184
+ expect(clause).toContain("MODIFY COLUMN");
185
+ expect(clause).toContain("`user_name`");
186
+ expect(clause).toContain("VARCHAR(100)");
186
187
  });
187
188
  });
188
189
 
189
- describe('isCompatibleTypeChange', () => {
190
- test('varchar -> text 是兼容变更', () => {
191
- expect(isCompatibleTypeChange('character varying', 'text')).toBe(true);
192
- expect(isCompatibleTypeChange('varchar(100)', 'text')).toBe(true);
193
- expect(isCompatibleTypeChange('varchar(100)', 'mediumtext')).toBe(true);
190
+ describe("isCompatibleTypeChange", () => {
191
+ test("varchar -> text 是兼容变更", () => {
192
+ expect(isCompatibleTypeChange("character varying", "text")).toBe(true);
193
+ expect(isCompatibleTypeChange("varchar(100)", "text")).toBe(true);
194
+ expect(isCompatibleTypeChange("varchar(100)", "mediumtext")).toBe(true);
194
195
  });
195
196
 
196
- test('text -> varchar 不是兼容变更', () => {
197
- expect(isCompatibleTypeChange('text', 'character varying')).toBe(false);
198
- expect(isCompatibleTypeChange('text', 'varchar(100)')).toBe(false);
197
+ test("text -> varchar 不是兼容变更", () => {
198
+ expect(isCompatibleTypeChange("text", "character varying")).toBe(false);
199
+ expect(isCompatibleTypeChange("text", "varchar(100)")).toBe(false);
199
200
  });
200
201
 
201
- test('int -> bigint 是兼容变更', () => {
202
- expect(isCompatibleTypeChange('int', 'bigint')).toBe(true);
203
- expect(isCompatibleTypeChange('int unsigned', 'bigint unsigned')).toBe(true);
204
- expect(isCompatibleTypeChange('tinyint', 'int')).toBe(true);
205
- expect(isCompatibleTypeChange('tinyint', 'bigint')).toBe(true);
206
- expect(isCompatibleTypeChange('smallint', 'int')).toBe(true);
207
- expect(isCompatibleTypeChange('mediumint', 'bigint')).toBe(true);
202
+ test("int -> bigint 是兼容变更", () => {
203
+ expect(isCompatibleTypeChange("int", "bigint")).toBe(true);
204
+ expect(isCompatibleTypeChange("int unsigned", "bigint unsigned")).toBe(true);
205
+ expect(isCompatibleTypeChange("tinyint", "int")).toBe(true);
206
+ expect(isCompatibleTypeChange("tinyint", "bigint")).toBe(true);
207
+ expect(isCompatibleTypeChange("smallint", "int")).toBe(true);
208
+ expect(isCompatibleTypeChange("mediumint", "bigint")).toBe(true);
208
209
  });
209
210
 
210
- test('bigint -> int 不是兼容变更(收缩)', () => {
211
- expect(isCompatibleTypeChange('bigint', 'int')).toBe(false);
212
- expect(isCompatibleTypeChange('int', 'tinyint')).toBe(false);
211
+ test("bigint -> int 不是兼容变更(收缩)", () => {
212
+ expect(isCompatibleTypeChange("bigint", "int")).toBe(false);
213
+ expect(isCompatibleTypeChange("int", "tinyint")).toBe(false);
213
214
  });
214
215
 
215
- test('PG integer -> bigint 是兼容变更', () => {
216
- expect(isCompatibleTypeChange('integer', 'bigint')).toBe(true);
217
- expect(isCompatibleTypeChange('smallint', 'integer')).toBe(true);
218
- expect(isCompatibleTypeChange('smallint', 'bigint')).toBe(true);
216
+ test("PG integer -> bigint 是兼容变更", () => {
217
+ expect(isCompatibleTypeChange("integer", "bigint")).toBe(true);
218
+ expect(isCompatibleTypeChange("smallint", "integer")).toBe(true);
219
+ expect(isCompatibleTypeChange("smallint", "bigint")).toBe(true);
219
220
  });
220
221
 
221
- test('相同类型不是变更', () => {
222
- expect(isCompatibleTypeChange('text', 'text')).toBe(false);
223
- expect(isCompatibleTypeChange('bigint', 'bigint')).toBe(false);
222
+ test("相同类型不是变更", () => {
223
+ expect(isCompatibleTypeChange("text", "text")).toBe(false);
224
+ expect(isCompatibleTypeChange("bigint", "bigint")).toBe(false);
224
225
  });
225
226
 
226
- test('空值处理', () => {
227
- expect(isCompatibleTypeChange(null, 'text')).toBe(false);
228
- expect(isCompatibleTypeChange('text', null)).toBe(false);
227
+ test("空值处理", () => {
228
+ expect(isCompatibleTypeChange(null, "text")).toBe(false);
229
+ expect(isCompatibleTypeChange("text", null)).toBe(false);
229
230
  });
230
231
  });
@@ -7,61 +7,62 @@
7
7
  * - applyFieldDefaults
8
8
  */
9
9
 
10
- import { describe, test, expect, beforeAll } from 'bun:test';
11
- import { setDbType } from '../sync/syncDb/constants.js';
10
+ import { describe, test, expect, beforeAll } from "bun:test";
11
+
12
+ import { setDbType } from "../sync/syncDb/constants.js";
12
13
 
13
14
  // 设置数据库类型为 MySQL
14
- setDbType('mysql');
15
+ setDbType("mysql");
15
16
 
16
17
  let quoteIdentifier: any;
17
18
  let escapeComment: any;
18
19
  let applyFieldDefaults: any;
19
20
 
20
21
  beforeAll(async () => {
21
- const helpers = await import('../sync/syncDb/helpers.js');
22
+ const helpers = await import("../sync/syncDb/helpers.js");
22
23
  quoteIdentifier = helpers.quoteIdentifier;
23
24
  escapeComment = helpers.escapeComment;
24
25
  applyFieldDefaults = helpers.applyFieldDefaults;
25
26
  });
26
27
 
27
- describe('quoteIdentifier (MySQL)', () => {
28
- test('使用反引号包裹标识符', () => {
29
- expect(quoteIdentifier('user_table')).toBe('`user_table`');
28
+ describe("quoteIdentifier (MySQL)", () => {
29
+ test("使用反引号包裹标识符", () => {
30
+ expect(quoteIdentifier("user_table")).toBe("`user_table`");
30
31
  });
31
32
 
32
- test('处理普通表名', () => {
33
- expect(quoteIdentifier('admin')).toBe('`admin`');
33
+ test("处理普通表名", () => {
34
+ expect(quoteIdentifier("admin")).toBe("`admin`");
34
35
  });
35
36
 
36
- test('处理带下划线的表名', () => {
37
- expect(quoteIdentifier('addon_admin_menu')).toBe('`addon_admin_menu`');
37
+ test("处理带下划线的表名", () => {
38
+ expect(quoteIdentifier("addon_admin_menu")).toBe("`addon_admin_menu`");
38
39
  });
39
40
  });
40
41
 
41
- describe('escapeComment', () => {
42
- test('普通注释不变', () => {
43
- expect(escapeComment('用户名称')).toBe('用户名称');
42
+ describe("escapeComment", () => {
43
+ test("普通注释不变", () => {
44
+ expect(escapeComment("用户名称")).toBe("用户名称");
44
45
  });
45
46
 
46
- test('双引号被转义', () => {
47
+ test("双引号被转义", () => {
47
48
  expect(escapeComment('用户"昵称"')).toBe('用户\\"昵称\\"');
48
49
  });
49
50
 
50
- test('空字符串', () => {
51
- expect(escapeComment('')).toBe('');
51
+ test("空字符串", () => {
52
+ expect(escapeComment("")).toBe("");
52
53
  });
53
54
  });
54
55
 
55
- describe('applyFieldDefaults', () => {
56
- test('为空字段定义应用默认值', () => {
56
+ describe("applyFieldDefaults", () => {
57
+ test("为空字段定义应用默认值", () => {
57
58
  const fieldDef: any = {
58
- name: '用户名',
59
- type: 'string'
59
+ name: "用户名",
60
+ type: "string"
60
61
  };
61
62
 
62
63
  applyFieldDefaults(fieldDef);
63
64
 
64
- expect(fieldDef.detail).toBe('');
65
+ expect(fieldDef.detail).toBe("");
65
66
  expect(fieldDef.min).toBe(0);
66
67
  expect(fieldDef.max).toBe(100);
67
68
  expect(fieldDef.default).toBe(null);
@@ -72,10 +73,10 @@ describe('applyFieldDefaults', () => {
72
73
  expect(fieldDef.regexp).toBe(null);
73
74
  });
74
75
 
75
- test('保留已有值', () => {
76
+ test("保留已有值", () => {
76
77
  const fieldDef: any = {
77
- name: '用户名',
78
- type: 'string',
78
+ name: "用户名",
79
+ type: "string",
79
80
  max: 200,
80
81
  index: true,
81
82
  unique: true,
@@ -90,10 +91,10 @@ describe('applyFieldDefaults', () => {
90
91
  expect(fieldDef.nullable).toBe(true);
91
92
  });
92
93
 
93
- test('处理 0 和 false 值', () => {
94
+ test("处理 0 和 false 值", () => {
94
95
  const fieldDef: any = {
95
- name: '排序',
96
- type: 'number',
96
+ name: "排序",
97
+ type: "number",
97
98
  min: 0,
98
99
  max: 0,
99
100
  default: 0,