befly 3.10.1 → 3.10.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.
Files changed (66) hide show
  1. package/.gitignore +0 -0
  2. package/configs/presetFields.ts +10 -0
  3. package/configs/presetRegexp.ts +225 -0
  4. package/package.json +15 -16
  5. package/tests/_mocks/mockSqliteDb.ts +0 -204
  6. package/tests/addonHelper-cache.test.ts +0 -32
  7. package/tests/api-integration-array-number.test.ts +0 -282
  8. package/tests/apiHandler-routePath-only.test.ts +0 -32
  9. package/tests/befly-config-env.test.ts +0 -78
  10. package/tests/cacheHelper.test.ts +0 -323
  11. package/tests/cacheKeys.test.ts +0 -41
  12. package/tests/checkApi-routePath-strict.test.ts +0 -166
  13. package/tests/checkMenu.test.ts +0 -346
  14. package/tests/checkTable-smoke.test.ts +0 -157
  15. package/tests/cipher.test.ts +0 -249
  16. package/tests/dbDialect-cache.test.ts +0 -23
  17. package/tests/dbDialect.test.ts +0 -46
  18. package/tests/dbHelper-advanced.test.ts +0 -723
  19. package/tests/dbHelper-all-array-types.test.ts +0 -316
  20. package/tests/dbHelper-array-serialization.test.ts +0 -258
  21. package/tests/dbHelper-batch-write.test.ts +0 -90
  22. package/tests/dbHelper-columns.test.ts +0 -234
  23. package/tests/dbHelper-execute.test.ts +0 -187
  24. package/tests/dbHelper-joins.test.ts +0 -221
  25. package/tests/fields-redis-cache.test.ts +0 -127
  26. package/tests/fields-validate.test.ts +0 -99
  27. package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +0 -3
  28. package/tests/fixtures/scanFilesApis/a.ts +0 -3
  29. package/tests/fixtures/scanFilesApis/sub/b.ts +0 -3
  30. package/tests/getClientIp.test.ts +0 -54
  31. package/tests/integration.test.ts +0 -189
  32. package/tests/jwt.test.ts +0 -65
  33. package/tests/loadPlugins-order-smoke.test.ts +0 -75
  34. package/tests/logger.test.ts +0 -325
  35. package/tests/redisHelper.test.ts +0 -495
  36. package/tests/redisKeys.test.ts +0 -9
  37. package/tests/scanConfig.test.ts +0 -144
  38. package/tests/scanFiles-routePath.test.ts +0 -46
  39. package/tests/smoke-sql.test.ts +0 -24
  40. package/tests/sqlBuilder-advanced.test.ts +0 -608
  41. package/tests/sqlBuilder.test.ts +0 -209
  42. package/tests/sync-connection.test.ts +0 -183
  43. package/tests/sync-init-guard.test.ts +0 -105
  44. package/tests/syncApi-insBatch-fields-consistent.test.ts +0 -61
  45. package/tests/syncApi-obsolete-records.test.ts +0 -69
  46. package/tests/syncApi-type-compat.test.ts +0 -72
  47. package/tests/syncDev-permissions.test.ts +0 -81
  48. package/tests/syncMenu-disableMenus-hard-delete.test.ts +0 -88
  49. package/tests/syncMenu-duplicate-path.test.ts +0 -122
  50. package/tests/syncMenu-obsolete-records.test.ts +0 -161
  51. package/tests/syncMenu-parentPath-from-tree.test.ts +0 -75
  52. package/tests/syncMenu-paths.test.ts +0 -59
  53. package/tests/syncTable-apply.test.ts +0 -279
  54. package/tests/syncTable-array-number.test.ts +0 -160
  55. package/tests/syncTable-constants.test.ts +0 -101
  56. package/tests/syncTable-db-integration.test.ts +0 -237
  57. package/tests/syncTable-ddl.test.ts +0 -245
  58. package/tests/syncTable-helpers.test.ts +0 -99
  59. package/tests/syncTable-schema.test.ts +0 -99
  60. package/tests/syncTable-testkit.test.ts +0 -25
  61. package/tests/syncTable-types.test.ts +0 -122
  62. package/tests/tableRef-and-deserialize.test.ts +0 -67
  63. package/tests/util.test.ts +0 -100
  64. package/tests/validator-array-number.test.ts +0 -310
  65. package/tests/validator-default.test.ts +0 -373
  66. package/tests/validator.test.ts +0 -679
@@ -1,282 +0,0 @@
1
- /**
2
- * API 集成测试 - 验证 array_number_string 类型在完整数据流中的表现
3
- *
4
- * 测试流程:请求验证 → 数据库存储 → 查询返回
5
- */
6
-
7
- import type { TableDefinition } from "befly/types/validate";
8
-
9
- import { describe, expect, test } from "bun:test";
10
-
11
- import { Validator } from "../lib/validator.js";
12
-
13
- describe("API 集成测试 - array_number_string 数据流", () => {
14
- // 模拟角色表定义
15
- const roleTable: TableDefinition = {
16
- name: {
17
- name: "角色名称",
18
- type: "string",
19
- min: 2,
20
- max: 50,
21
- default: null,
22
- regexp: null
23
- },
24
- code: {
25
- name: "角色编码",
26
- type: "string",
27
- min: 2,
28
- max: 50,
29
- default: null,
30
- regexp: null
31
- },
32
- menus: {
33
- name: "菜单权限",
34
- type: "array_number_string",
35
- min: null,
36
- max: 2000,
37
- default: null,
38
- regexp: null
39
- },
40
- apis: {
41
- name: "接口权限",
42
- type: "array_number_string",
43
- min: null,
44
- max: 3000,
45
- default: null,
46
- regexp: null
47
- }
48
- };
49
-
50
- // ==================== 请求验证阶段 ====================
51
-
52
- test("请求验证: 接受合法的数字数组", () => {
53
- const requestData = {
54
- name: "测试角色",
55
- code: "test_role",
56
- menus: [1, 2, 3, 4, 5],
57
- apis: [10, 20, 30]
58
- };
59
-
60
- const result = Validator.validate(requestData, roleTable, ["name", "code"]);
61
- expect(result.failed).toBe(false);
62
- });
63
-
64
- test("请求验证: 拒绝字符串数组", () => {
65
- const requestData = {
66
- name: "测试角色",
67
- code: "test_role",
68
- menus: ["1", "2", "3"], // 错误:字符串数组
69
- apis: [10, 20, 30]
70
- };
71
-
72
- const result = Validator.validate(requestData, roleTable, ["name", "code"]);
73
- expect(result.failed).toBe(true);
74
- expect(result.firstError).toContain("数组元素必须是数字");
75
- });
76
-
77
- test("请求验证: 拒绝混合类型数组", () => {
78
- const requestData = {
79
- name: "测试角色",
80
- code: "test_role",
81
- menus: [1, 2, 3],
82
- apis: [10, "20", 30] // 错误:混合类型
83
- };
84
-
85
- const result = Validator.validate(requestData, roleTable, ["name", "code"]);
86
- expect(result.failed).toBe(true);
87
- expect(result.firstError).toContain("数组元素必须是数字");
88
- });
89
-
90
- test("请求验证: 空数组应通过验证", () => {
91
- const requestData = {
92
- name: "测试角色",
93
- code: "test_role",
94
- menus: [],
95
- apis: []
96
- };
97
-
98
- const result = Validator.validate(requestData, roleTable, ["name", "code"]);
99
- expect(result.failed).toBe(false);
100
- });
101
-
102
- test("请求验证: 未传递数组字段时使用默认值", () => {
103
- const requestData = {
104
- name: "测试角色",
105
- code: "test_role"
106
- // menus 和 apis 未传递
107
- };
108
-
109
- const result = Validator.validate(requestData, roleTable, ["name", "code"]);
110
- expect(result.failed).toBe(false);
111
- });
112
-
113
- test("请求验证: max 元素数量限制生效", () => {
114
- // 注意:max 参数对 array_number_string 是限制数组长度,不是字符串长度
115
- const roleTableWithMax: TableDefinition = {
116
- ...roleTable,
117
- menus: {
118
- name: "菜单权限",
119
- type: "array_number_string",
120
- min: null,
121
- max: 10, // 最多 10 个元素
122
- default: null,
123
- regexp: null
124
- }
125
- };
126
-
127
- const requestData = {
128
- name: "测试角色",
129
- code: "test_role",
130
- menus: Array.from({ length: 15 }, (_, i) => i), // 15 个元素,超过限制
131
- apis: [10, 20, 30]
132
- };
133
-
134
- const result = Validator.validate(requestData, roleTableWithMax, ["name", "code"]);
135
- expect(result.failed).toBe(true);
136
- expect(result.firstError).toContain("最多只能有10个元素");
137
- });
138
-
139
- // ==================== 数据库存储模拟 ====================
140
-
141
- test("数据库存储: 数字数组应被正确序列化", () => {
142
- const menuIds = [1, 2, 3, 4, 5];
143
- const apiIds = [10, 20, 30, 40];
144
-
145
- // 模拟数据库存储时的序列化(实际由 dbHelper 处理)
146
- const serializedMenus = JSON.stringify(menuIds);
147
- const serializedApis = JSON.stringify(apiIds);
148
-
149
- expect(serializedMenus).toBe("[1,2,3,4,5]");
150
- expect(serializedApis).toBe("[10,20,30,40]");
151
- });
152
-
153
- test('数据库存储: 空数组应被序列化为 "[]"', () => {
154
- const menuIds: number[] = [];
155
-
156
- const serialized = JSON.stringify(menuIds);
157
- expect(serialized).toBe("[]");
158
- });
159
-
160
- // ==================== 查询返回模拟 ====================
161
-
162
- test("查询返回: 数据库字符串应被正确解析为数组", () => {
163
- // 模拟从数据库查询返回的数据(实际由 dbHelper 处理)
164
- const dbRow = {
165
- id: 1,
166
- name: "测试角色",
167
- code: "test_role",
168
- menus: "[1,2,3,4,5]",
169
- apis: "[10,20,30,40]"
170
- };
171
-
172
- // 解析
173
- const parsedMenus = JSON.parse(dbRow.menus);
174
- const parsedApis = JSON.parse(dbRow.apis);
175
-
176
- expect(Array.isArray(parsedMenus)).toBe(true);
177
- expect(Array.isArray(parsedApis)).toBe(true);
178
- expect(parsedMenus).toEqual([1, 2, 3, 4, 5]);
179
- expect(parsedApis).toEqual([10, 20, 30, 40]);
180
- });
181
-
182
- test('查询返回: 空数组字符串 "[]" 应解析为空数组', () => {
183
- const dbRow = {
184
- id: 1,
185
- name: "测试角色",
186
- code: "test_role",
187
- menus: "[]",
188
- apis: "[]"
189
- };
190
-
191
- const parsedMenus = JSON.parse(dbRow.menus);
192
- const parsedApis = JSON.parse(dbRow.apis);
193
-
194
- expect(parsedMenus).toEqual([]);
195
- expect(parsedApis).toEqual([]);
196
- });
197
-
198
- // ==================== 完整数据流测试 ====================
199
-
200
- test("完整流程: 请求 → 验证 → 存储 → 查询 → 返回", () => {
201
- // 1. 模拟 API 请求
202
- const requestData = {
203
- name: "管理员角色",
204
- code: "admin",
205
- menus: [1, 2, 3, 10, 11, 12],
206
- apis: [100, 101, 102, 200, 201]
207
- };
208
-
209
- // 2. 验证请求数据
210
- const validationResult = Validator.validate(requestData, roleTable, ["name", "code"]);
211
- expect(validationResult.failed).toBe(false);
212
-
213
- // 3. 模拟数据库存储(序列化)
214
- const dbData = {
215
- name: requestData.name,
216
- code: requestData.code,
217
- menus: JSON.stringify(requestData.menus),
218
- apis: JSON.stringify(requestData.apis)
219
- };
220
-
221
- expect(dbData.menus).toBe("[1,2,3,10,11,12]");
222
- expect(dbData.apis).toBe("[100,101,102,200,201]");
223
-
224
- // 4. 模拟查询返回(反序列化)
225
- const responseData = {
226
- id: 1,
227
- name: dbData.name,
228
- code: dbData.code,
229
- menus: JSON.parse(dbData.menus),
230
- apis: JSON.parse(dbData.apis)
231
- };
232
-
233
- // 5. 验证返回数据
234
- expect(responseData.menus).toEqual(requestData.menus);
235
- expect(responseData.apis).toEqual(requestData.apis);
236
- });
237
-
238
- test("完整流程: 更新权限(部分字段)", () => {
239
- // 模拟更新操作,只更新 menus
240
- const existingData = {
241
- id: 1,
242
- name: "管理员角色",
243
- code: "admin",
244
- menus: "[1,2,3]",
245
- apis: "[100,101,102]"
246
- };
247
-
248
- const updateRequest = {
249
- id: 1,
250
- menus: [1, 2, 3, 4, 5, 6] // 新增菜单
251
- };
252
-
253
- // 验证更新数据
254
- const fields: TableDefinition = {
255
- menus: roleTable.menus
256
- };
257
- const validationResult = Validator.validate(updateRequest, fields, []);
258
- expect(validationResult.failed).toBe(false);
259
-
260
- // 更新后的数据
261
- const updatedData = {
262
- ...existingData,
263
- menus: JSON.stringify(updateRequest.menus)
264
- };
265
-
266
- expect(updatedData.menus).toBe("[1,2,3,4,5,6]");
267
- expect(updatedData.apis).toBe("[100,101,102]"); // 未变
268
- });
269
-
270
- test("错误场景: 尝试存储非法数据", () => {
271
- const invalidRequest = {
272
- name: "测试角色",
273
- code: "test",
274
- menus: "invalid", // 错误:不是数组
275
- apis: [10, 20]
276
- };
277
-
278
- const result = Validator.validate(invalidRequest, roleTable, ["name", "code"]);
279
- expect(result.failed).toBe(true);
280
- expect(result.errorFields).toContain("menus");
281
- });
282
- });
@@ -1,32 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { apiHandler } from "../router/api.js";
4
-
5
- describe("apiHandler - route matching", () => {
6
- test("接口存在性判断应仅使用 url.pathname(不附加 method)", async () => {
7
- const apis = new Map<string, any>();
8
-
9
- apis.set("/api/hello", {
10
- name: "hello",
11
- method: "POST",
12
- handler: async () => {
13
- return {
14
- msg: "ok"
15
- };
16
- }
17
- });
18
-
19
- const hooks: any[] = [];
20
- const context: any = {};
21
-
22
- const handler = apiHandler(apis as any, hooks as any, context as any);
23
-
24
- // 如果 key 里拼了 method,这里会变成 "POST /api/hello" 之类,从而导致接口不存在
25
- const res = await handler(new Request("http://localhost/api/hello", { method: "POST" }));
26
- expect(res.status).toBe(200);
27
-
28
- const body = await res.json();
29
- expect(body.code).toBe(0);
30
- expect(body.msg).toBe("ok");
31
- });
32
- });
@@ -1,78 +0,0 @@
1
- import { describe, test, expect } from "bun:test";
2
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
-
5
- import { loadBeflyConfig } from "../befly.config.js";
6
-
7
- describe("Config - NODE_ENV 选择 development/production", () => {
8
- test("production -> befly.production.json;其他 -> befly.development.json(common 总是生效)", async () => {
9
- const tempRoot = join(process.cwd(), "temp", `befly-config-env-test-${Date.now()}-${Math.random().toString(16).slice(2)}`);
10
- const tempConfigsDir = join(tempRoot, "configs");
11
-
12
- mkdirSync(tempConfigsDir, { recursive: true });
13
-
14
- writeFileSync(
15
- join(tempConfigsDir, "befly.common.json"),
16
- JSON.stringify(
17
- {
18
- appPort: 31111,
19
- logger: {
20
- debug: 2
21
- }
22
- },
23
- null,
24
- 4
25
- ),
26
- { encoding: "utf8" }
27
- );
28
-
29
- writeFileSync(
30
- join(tempConfigsDir, "befly.development.json"),
31
- JSON.stringify(
32
- {
33
- appName: "development",
34
- logger: {
35
- debug: 3
36
- }
37
- },
38
- null,
39
- 4
40
- ),
41
- { encoding: "utf8" }
42
- );
43
-
44
- writeFileSync(
45
- join(tempConfigsDir, "befly.production.json"),
46
- JSON.stringify(
47
- {
48
- appName: "production",
49
- logger: {
50
- debug: 9
51
- }
52
- },
53
- null,
54
- 4
55
- ),
56
- { encoding: "utf8" }
57
- );
58
-
59
- try {
60
- const productionConfig = await loadBeflyConfig({ cwd: tempRoot, nodeEnv: "production" });
61
- expect(productionConfig.appName).toBe("production");
62
- expect(productionConfig.logger.debug).toBe(9);
63
- expect(productionConfig.appPort).toBe(31111);
64
-
65
- const developmentConfig = await loadBeflyConfig({ cwd: tempRoot, nodeEnv: "development" });
66
- expect(developmentConfig.appName).toBe("development");
67
- expect(developmentConfig.logger.debug).toBe(3);
68
- expect(developmentConfig.appPort).toBe(31111);
69
-
70
- const fallbackConfig = await loadBeflyConfig({ cwd: tempRoot, nodeEnv: "test" });
71
- expect(fallbackConfig.appName).toBe("development");
72
- expect(fallbackConfig.logger.debug).toBe(3);
73
- expect(fallbackConfig.appPort).toBe(31111);
74
- } finally {
75
- rmSync(tempRoot, { recursive: true, force: true });
76
- }
77
- });
78
- });