befly 3.10.1 → 3.10.3

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,346 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
-
5
- import { checkMenu } from "../checks/checkMenu.js";
6
-
7
- describe("checkMenu", () => {
8
- test("重复 path 应阻断菜单同步", async () => {
9
- const originalCwd = process.cwd();
10
- const projectDir = join(originalCwd, "temp", `checkMenu-dup-${Date.now()}-${Math.random().toString(16).slice(2)}`);
11
- const menusJsonPath = join(projectDir, "menus.json");
12
-
13
- try {
14
- mkdirSync(projectDir, { recursive: true });
15
- process.chdir(projectDir);
16
-
17
- writeFileSync(
18
- menusJsonPath,
19
- JSON.stringify(
20
- [
21
- { name: "A", path: "/a", sort: 1 },
22
- { name: "B", path: "/a", sort: 2 }
23
- ],
24
- null,
25
- 4
26
- ),
27
- { encoding: "utf8" }
28
- );
29
-
30
- let thrown: any = null;
31
- try {
32
- await checkMenu([]);
33
- } catch (error: any) {
34
- thrown = error;
35
- }
36
-
37
- expect(thrown).toBeTruthy();
38
- expect(thrown.message).toBe("菜单结构检查失败");
39
- } finally {
40
- process.chdir(originalCwd);
41
- rmSync(projectDir, { recursive: true, force: true });
42
- }
43
- });
44
-
45
- test("缺失父级 path 应阻断菜单同步", async () => {
46
- const originalCwd = process.cwd();
47
- const projectDir = join(originalCwd, "temp", `checkMenu-parent-${Date.now()}-${Math.random().toString(16).slice(2)}`);
48
- const menusJsonPath = join(projectDir, "menus.json");
49
-
50
- try {
51
- mkdirSync(projectDir, { recursive: true });
52
- process.chdir(projectDir);
53
-
54
- writeFileSync(menusJsonPath, JSON.stringify([{ name: "B", path: "/a/b", sort: 1 }], null, 4), { encoding: "utf8" });
55
-
56
- // 菜单层级应以配置树(children)为准,而非按 URL path 分段强制推导父级。
57
- // 因此单个菜单(即使 path 含多段)也允许作为根级菜单存在。
58
- const menus = await checkMenu([]);
59
- expect(Array.isArray(menus)).toBe(true);
60
- expect(menus).toHaveLength(1);
61
- expect(menus[0]?.path).toBe("/a/b");
62
- } finally {
63
- process.chdir(originalCwd);
64
- rmSync(projectDir, { recursive: true, force: true });
65
- }
66
- });
67
-
68
- test("disableMenus(精确)应过滤指定菜单", async () => {
69
- const originalCwd = process.cwd();
70
- const projectDir = join(originalCwd, "temp", `checkMenu-disableMenus-exact-${Date.now()}-${Math.random().toString(16).slice(2)}`);
71
- const menusJsonPath = join(projectDir, "menus.json");
72
-
73
- try {
74
- mkdirSync(projectDir, { recursive: true });
75
- process.chdir(projectDir);
76
-
77
- writeFileSync(
78
- menusJsonPath,
79
- JSON.stringify(
80
- [
81
- {
82
- name: "A",
83
- path: "/a",
84
- sort: 1,
85
- children: [
86
- {
87
- name: "B",
88
- path: "/a/b",
89
- sort: 2
90
- }
91
- ]
92
- },
93
- {
94
- name: "C",
95
- path: "/c",
96
- sort: 3
97
- }
98
- ],
99
- null,
100
- 4
101
- ),
102
- { encoding: "utf8" }
103
- );
104
-
105
- const menus = await checkMenu([], { disableMenus: ["/a/b"] });
106
- expect(menus).toHaveLength(2);
107
- expect(menus[0]?.path).toBe("/a");
108
- expect(menus[1]?.path).toBe("/c");
109
- expect(Array.isArray((menus[0] as any)?.children)).toBe(false);
110
- } finally {
111
- process.chdir(originalCwd);
112
- rmSync(projectDir, { recursive: true, force: true });
113
- }
114
- });
115
-
116
- test("disableMenus(glob)应按 Bun.Glob 语义过滤匹配的菜单", async () => {
117
- const originalCwd = process.cwd();
118
- const projectDir = join(originalCwd, "temp", `checkMenu-disableMenus-glob-${Date.now()}-${Math.random().toString(16).slice(2)}`);
119
- const menusJsonPath = join(projectDir, "menus.json");
120
-
121
- try {
122
- mkdirSync(projectDir, { recursive: true });
123
- process.chdir(projectDir);
124
-
125
- writeFileSync(
126
- menusJsonPath,
127
- JSON.stringify(
128
- [
129
- { name: "A", path: "/a", sort: 1 },
130
- { name: "A-1", path: "/a/1", sort: 2 },
131
- { name: "B", path: "/b", sort: 3 }
132
- ],
133
- null,
134
- 4
135
- ),
136
- { encoding: "utf8" }
137
- );
138
-
139
- // 注意:此处完全遵循 Bun.Glob 的 match 语义。
140
- // 通常 "*" 不跨路径分隔符,因此 "/a/*" 仅匹配 "/a/1",不会匹配 "/a"。
141
- const menus = await checkMenu([], { disableMenus: ["/a/*"] });
142
- const paths = menus.map((m) => m.path);
143
- expect(paths).toContain("/a");
144
- expect(paths).toContain("/b");
145
- expect(paths).not.toContain("/a/1");
146
- } finally {
147
- process.chdir(originalCwd);
148
- rmSync(projectDir, { recursive: true, force: true });
149
- }
150
- });
151
-
152
- test("disableMenus 规则不合法应阻断启动", async () => {
153
- const originalCwd = process.cwd();
154
- const projectDir = join(originalCwd, "temp", `checkMenu-disableMenus-invalid-${Date.now()}-${Math.random().toString(16).slice(2)}`);
155
- const menusJsonPath = join(projectDir, "menus.json");
156
-
157
- try {
158
- mkdirSync(projectDir, { recursive: true });
159
- process.chdir(projectDir);
160
-
161
- writeFileSync(menusJsonPath, JSON.stringify([{ name: "A", path: "/a", sort: 1 }], null, 4), { encoding: "utf8" });
162
-
163
- // 1) disableMenus 必须是数组
164
- {
165
- let thrown: any = null;
166
- try {
167
- await checkMenu([], { disableMenus: "not-array" as any });
168
- } catch (error: any) {
169
- thrown = error;
170
- }
171
- expect(thrown).toBeTruthy();
172
- expect(String(thrown.message).includes("disableMenus")).toBe(true);
173
- }
174
-
175
- // 2) 数组元素必须是 string
176
- {
177
- let thrown: any = null;
178
- try {
179
- await checkMenu([], { disableMenus: [123 as any] });
180
- } catch (error: any) {
181
- thrown = error;
182
- }
183
- expect(thrown).toBeTruthy();
184
- expect(String(thrown.message).includes("disableMenus")).toBe(true);
185
- }
186
-
187
- // 3) 不允许空字符串
188
- {
189
- let thrown: any = null;
190
- try {
191
- await checkMenu([], { disableMenus: [" "] });
192
- } catch (error: any) {
193
- thrown = error;
194
- }
195
- expect(thrown).toBeTruthy();
196
- expect(String(thrown.message).includes("disableMenus")).toBe(true);
197
- }
198
- } finally {
199
- process.chdir(originalCwd);
200
- rmSync(projectDir, { recursive: true, force: true });
201
- }
202
- });
203
-
204
- test("默认应屏蔽 /404 /403 /500 以及所有以 /login 结尾的菜单路由", async () => {
205
- const originalCwd = process.cwd();
206
- const projectDir = join(originalCwd, "temp", `checkMenu-default-disable-${Date.now()}-${Math.random().toString(16).slice(2)}`);
207
- const menusJsonPath = join(projectDir, "menus.json");
208
-
209
- try {
210
- mkdirSync(projectDir, { recursive: true });
211
- process.chdir(projectDir);
212
-
213
- writeFileSync(
214
- menusJsonPath,
215
- JSON.stringify(
216
- [
217
- { name: "Login", path: "/login", sort: 1 },
218
- { name: "AddonLogin", path: "/addon/admin/login", sort: 2 },
219
- { name: "404", path: "/404", sort: 3 },
220
- { name: "403", path: "/403", sort: 4 },
221
- { name: "500", path: "/500", sort: 5 },
222
- { name: "A", path: "/a", sort: 6 }
223
- ],
224
- null,
225
- 4
226
- ),
227
- { encoding: "utf8" }
228
- );
229
-
230
- const menus = await checkMenu([], { disableMenus: ["**/404", "**/403", "**/500", "**/login"] });
231
- expect(Array.isArray(menus)).toBe(true);
232
- expect(menus).toHaveLength(1);
233
- expect(menus[0]?.path).toBe("/a");
234
- } finally {
235
- process.chdir(originalCwd);
236
- rmSync(projectDir, { recursive: true, force: true });
237
- }
238
- });
239
-
240
- test("合法 menus.json 应通过检查", async () => {
241
- const originalCwd = process.cwd();
242
- const projectDir = join(originalCwd, "temp", `checkMenu-ok-${Date.now()}-${Math.random().toString(16).slice(2)}`);
243
- const menusJsonPath = join(projectDir, "menus.json");
244
-
245
- try {
246
- mkdirSync(projectDir, { recursive: true });
247
- process.chdir(projectDir);
248
-
249
- writeFileSync(menusJsonPath, JSON.stringify([{ name: "A", path: "/a", sort: 1, children: [{ name: "B", path: "/a/b", sort: 2 }] }], null, 4), { encoding: "utf8" });
250
-
251
- const menus = await checkMenu([]);
252
- expect(Array.isArray(menus)).toBe(true);
253
- expect(menus.length).toBe(1);
254
- } finally {
255
- process.chdir(originalCwd);
256
- rmSync(projectDir, { recursive: true, force: true });
257
- }
258
- });
259
-
260
- test("超过三级菜单应阻断同步", async () => {
261
- const originalCwd = process.cwd();
262
- const projectDir = join(originalCwd, "temp", `checkMenu-depth-${Date.now()}-${Math.random().toString(16).slice(2)}`);
263
- const menusJsonPath = join(projectDir, "menus.json");
264
-
265
- try {
266
- mkdirSync(projectDir, { recursive: true });
267
- process.chdir(projectDir);
268
-
269
- writeFileSync(
270
- menusJsonPath,
271
- JSON.stringify(
272
- [
273
- {
274
- name: "A",
275
- path: "/a",
276
- sort: 1,
277
- children: [
278
- {
279
- name: "B",
280
- path: "/a/b",
281
- sort: 2,
282
- children: [
283
- {
284
- name: "C",
285
- path: "/a/b/c",
286
- sort: 3,
287
- children: [
288
- {
289
- name: "D",
290
- path: "/a/b/c/d",
291
- sort: 4
292
- }
293
- ]
294
- }
295
- ]
296
- }
297
- ]
298
- }
299
- ],
300
- null,
301
- 4
302
- ),
303
- { encoding: "utf8" }
304
- );
305
-
306
- let thrown: any = null;
307
- try {
308
- await checkMenu([]);
309
- } catch (error: any) {
310
- thrown = error;
311
- }
312
-
313
- expect(thrown).toBeTruthy();
314
- expect(thrown.message).toBe("菜单结构检查失败");
315
- } finally {
316
- process.chdir(originalCwd);
317
- rmSync(projectDir, { recursive: true, force: true });
318
- }
319
- });
320
-
321
- test("sort 最小值应为 1(sort=0 应阻断启动)", async () => {
322
- const originalCwd = process.cwd();
323
- const projectDir = join(originalCwd, "temp", `checkMenu-sort-min-${Date.now()}-${Math.random().toString(16).slice(2)}`);
324
- const menusJsonPath = join(projectDir, "menus.json");
325
-
326
- try {
327
- mkdirSync(projectDir, { recursive: true });
328
- process.chdir(projectDir);
329
-
330
- writeFileSync(menusJsonPath, JSON.stringify([{ name: "A", path: "/a", sort: 0 }], null, 4), { encoding: "utf8" });
331
-
332
- let thrown: any = null;
333
- try {
334
- await checkMenu([]);
335
- } catch (error: any) {
336
- thrown = error;
337
- }
338
-
339
- expect(thrown).toBeTruthy();
340
- expect(thrown.message).toBe("菜单结构检查失败");
341
- } finally {
342
- process.chdir(originalCwd);
343
- rmSync(projectDir, { recursive: true, force: true });
344
- }
345
- });
346
- });
@@ -1,157 +0,0 @@
1
- import type { ScanFileResult } from "../utils/scanFiles.js";
2
-
3
- import { describe, expect, test } from "bun:test";
4
-
5
- import { checkTable } from "../checks/checkTable.js";
6
- import { Logger } from "../lib/logger.js";
7
-
8
- describe("checkTable - smoke", () => {
9
- test("应忽略非 table 项;合法表定义不应抛错", async () => {
10
- const items: ScanFileResult[] = [
11
- {
12
- type: "api",
13
- source: "app",
14
- sourceName: "项目",
15
- filePath: "DUMMY",
16
- relativePath: "DUMMY",
17
- fileName: "dummy",
18
- moduleName: "app_dummy",
19
- addonName: "",
20
- content: {}
21
- } as any,
22
- {
23
- type: "table",
24
- source: "app",
25
- sourceName: "项目",
26
- filePath: "DUMMY",
27
- relativePath: "testCustomers",
28
- fileName: "testCustomers",
29
- moduleName: "app_testCustomers",
30
- addonName: "",
31
- content: {
32
- customerName: { name: "客户名", type: "string", max: 32 }
33
- }
34
- } as any
35
- ];
36
-
37
- await checkTable(items);
38
- expect(true).toBe(true);
39
- });
40
-
41
- test("unique 和 index 同时为 true 时应阻断启动(抛错)", async () => {
42
- const items: ScanFileResult[] = [
43
- {
44
- type: "table",
45
- source: "app",
46
- sourceName: "项目",
47
- filePath: "DUMMY",
48
- relativePath: "testMenu",
49
- fileName: "testMenu",
50
- moduleName: "app_testMenu",
51
- addonName: "",
52
- content: {
53
- path: { name: "路径", type: "string", max: 128, unique: true, index: true }
54
- }
55
- } as any
56
- ];
57
-
58
- let thrownError: any = null;
59
- try {
60
- await checkTable(items);
61
- } catch (error: any) {
62
- thrownError = error;
63
- }
64
-
65
- expect(Boolean(thrownError)).toBe(true);
66
- expect(String(thrownError?.message || "")).toContain("表结构检查失败");
67
- });
68
-
69
- test("sourceName 缺失时:日志不应出现 undefined表(允许前缀为空)", async () => {
70
- const calls: Array<{ level: string; args: unknown[] }> = [];
71
- const mockLogger = {
72
- info(...args: unknown[]) {
73
- calls.push({ level: "info", args: args });
74
- },
75
- warn(...args: unknown[]) {
76
- calls.push({ level: "warn", args: args });
77
- },
78
- error(...args: unknown[]) {
79
- calls.push({ level: "error", args: args });
80
- },
81
- debug(...args: unknown[]) {
82
- calls.push({ level: "debug", args: args });
83
- }
84
- } as any;
85
-
86
- Logger.setMock(mockLogger);
87
-
88
- try {
89
- await checkTable([
90
- {
91
- type: "table",
92
- source: "app",
93
- filePath: "DUMMY",
94
- relativePath: "TestCustomers",
95
- fileName: "TestCustomers",
96
- moduleName: "app_TestCustomers",
97
- addonName: "",
98
- content: {}
99
- } as any
100
- ]);
101
- } catch {
102
- // 触发 hasError 后会抛错:这里只验证日志前缀
103
- } finally {
104
- Logger.setMock(null);
105
- }
106
-
107
- const warnMessages = calls.filter((item) => item.level === "warn").map((item) => String(item.args[0]));
108
-
109
- expect(warnMessages.some((msg) => msg.includes("表 TestCustomers"))).toBe(true);
110
- expect(warnMessages.some((msg) => msg.includes("undefined表"))).toBe(false);
111
- });
112
-
113
- test("sourceName 非字符串时:日志不应出现 undefined表(允许前缀为空)", async () => {
114
- const calls: Array<{ level: string; args: unknown[] }> = [];
115
- const mockLogger = {
116
- info(...args: unknown[]) {
117
- calls.push({ level: "info", args: args });
118
- },
119
- warn(...args: unknown[]) {
120
- calls.push({ level: "warn", args: args });
121
- },
122
- error(...args: unknown[]) {
123
- calls.push({ level: "error", args: args });
124
- },
125
- debug(...args: unknown[]) {
126
- calls.push({ level: "debug", args: args });
127
- }
128
- } as any;
129
-
130
- Logger.setMock(mockLogger);
131
-
132
- try {
133
- await checkTable([
134
- {
135
- type: "table",
136
- source: "app",
137
- sourceName: 123,
138
- filePath: "DUMMY",
139
- relativePath: "TestCustomers",
140
- fileName: "TestCustomers",
141
- moduleName: "app_TestCustomers",
142
- addonName: "",
143
- content: {}
144
- } as any
145
- ]);
146
- } catch {
147
- // 触发 hasError 后会抛错:这里只验证日志前缀
148
- } finally {
149
- Logger.setMock(null);
150
- }
151
-
152
- const warnMessages = calls.filter((item) => item.level === "warn").map((item) => String(item.args[0]));
153
-
154
- expect(warnMessages.some((msg) => msg.includes("表 TestCustomers"))).toBe(true);
155
- expect(warnMessages.some((msg) => msg.includes("undefined表"))).toBe(false);
156
- });
157
- });