befly 3.9.40 → 3.10.1

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 (144) hide show
  1. package/README.md +47 -19
  2. package/befly.config.ts +19 -2
  3. package/checks/checkApi.ts +79 -77
  4. package/checks/checkHook.ts +48 -0
  5. package/checks/checkMenu.ts +168 -0
  6. package/checks/checkPlugin.ts +48 -0
  7. package/checks/checkTable.ts +137 -183
  8. package/docs/README.md +17 -11
  9. package/docs/api/api.md +16 -2
  10. package/docs/guide/quickstart.md +31 -10
  11. package/docs/hooks/hook.md +2 -2
  12. package/docs/hooks/rateLimit.md +1 -1
  13. package/docs/infra/redis.md +26 -14
  14. package/docs/plugins/plugin.md +23 -21
  15. package/docs/quickstart.md +5 -328
  16. package/docs/reference/addon.md +0 -4
  17. package/docs/reference/config.md +14 -31
  18. package/docs/reference/logger.md +3 -3
  19. package/docs/reference/sync.md +132 -237
  20. package/docs/reference/table.md +28 -30
  21. package/hooks/auth.ts +3 -4
  22. package/hooks/cors.ts +4 -6
  23. package/hooks/parser.ts +3 -4
  24. package/hooks/permission.ts +3 -4
  25. package/hooks/validator.ts +3 -4
  26. package/lib/cacheHelper.ts +89 -153
  27. package/lib/cacheKeys.ts +1 -1
  28. package/lib/connect.ts +9 -13
  29. package/lib/dbDialect.ts +285 -0
  30. package/lib/dbHelper.ts +179 -507
  31. package/lib/dbUtils.ts +450 -0
  32. package/lib/logger.ts +41 -5
  33. package/lib/redisHelper.ts +1 -0
  34. package/lib/sqlBuilder.ts +358 -58
  35. package/lib/sqlCheck.ts +136 -0
  36. package/lib/validator.ts +1 -1
  37. package/loader/loadApis.ts +23 -126
  38. package/loader/loadHooks.ts +31 -46
  39. package/loader/loadPlugins.ts +37 -52
  40. package/main.ts +58 -19
  41. package/package.json +24 -25
  42. package/paths.ts +14 -14
  43. package/plugins/cache.ts +12 -6
  44. package/plugins/cipher.ts +2 -2
  45. package/plugins/config.ts +6 -8
  46. package/plugins/db.ts +14 -19
  47. package/plugins/jwt.ts +6 -7
  48. package/plugins/logger.ts +7 -9
  49. package/plugins/redis.ts +8 -10
  50. package/plugins/tool.ts +3 -4
  51. package/router/api.ts +3 -2
  52. package/router/static.ts +7 -5
  53. package/sync/syncApi.ts +80 -235
  54. package/sync/syncCache.ts +16 -0
  55. package/sync/syncDev.ts +167 -202
  56. package/sync/syncMenu.ts +230 -444
  57. package/sync/syncTable.ts +1247 -0
  58. package/tests/_mocks/mockSqliteDb.ts +204 -0
  59. package/tests/addonHelper-cache.test.ts +32 -0
  60. package/tests/apiHandler-routePath-only.test.ts +32 -0
  61. package/tests/cacheHelper.test.ts +16 -51
  62. package/tests/checkApi-routePath-strict.test.ts +166 -0
  63. package/tests/checkMenu.test.ts +346 -0
  64. package/tests/checkTable-smoke.test.ts +157 -0
  65. package/tests/dbDialect-cache.test.ts +23 -0
  66. package/tests/dbDialect.test.ts +46 -0
  67. package/tests/dbHelper-advanced.test.ts +1 -1
  68. package/tests/dbHelper-all-array-types.test.ts +15 -15
  69. package/tests/dbHelper-batch-write.test.ts +90 -0
  70. package/tests/dbHelper-columns.test.ts +36 -54
  71. package/tests/dbHelper-execute.test.ts +26 -26
  72. package/tests/dbHelper-joins.test.ts +85 -176
  73. package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +3 -0
  74. package/tests/fixtures/scanFilesApis/a.ts +3 -0
  75. package/tests/fixtures/scanFilesApis/sub/b.ts +3 -0
  76. package/tests/loadPlugins-order-smoke.test.ts +75 -0
  77. package/tests/logger.test.ts +6 -6
  78. package/tests/redisHelper.test.ts +6 -1
  79. package/tests/scanFiles-routePath.test.ts +46 -0
  80. package/tests/smoke-sql.test.ts +24 -0
  81. package/tests/sqlBuilder-advanced.test.ts +18 -5
  82. package/tests/sqlBuilder.test.ts +24 -0
  83. package/tests/sync-init-guard.test.ts +105 -0
  84. package/tests/syncApi-insBatch-fields-consistent.test.ts +61 -0
  85. package/tests/syncApi-obsolete-records.test.ts +69 -0
  86. package/tests/syncApi-type-compat.test.ts +72 -0
  87. package/tests/syncDev-permissions.test.ts +81 -0
  88. package/tests/syncMenu-disableMenus-hard-delete.test.ts +88 -0
  89. package/tests/syncMenu-duplicate-path.test.ts +122 -0
  90. package/tests/syncMenu-obsolete-records.test.ts +161 -0
  91. package/tests/syncMenu-parentPath-from-tree.test.ts +75 -0
  92. package/tests/syncMenu-paths.test.ts +0 -9
  93. package/tests/{syncDb-apply.test.ts → syncTable-apply.test.ts} +14 -24
  94. package/tests/{syncDb-array-number.test.ts → syncTable-array-number.test.ts} +31 -31
  95. package/tests/syncTable-constants.test.ts +101 -0
  96. package/tests/syncTable-db-integration.test.ts +237 -0
  97. package/tests/{syncDb-ddl.test.ts → syncTable-ddl.test.ts} +67 -53
  98. package/tests/{syncDb-helpers.test.ts → syncTable-helpers.test.ts} +12 -26
  99. package/tests/syncTable-schema.test.ts +99 -0
  100. package/tests/syncTable-testkit.test.ts +25 -0
  101. package/tests/syncTable-types.test.ts +122 -0
  102. package/tests/tableRef-and-deserialize.test.ts +67 -0
  103. package/tsconfig.json +1 -1
  104. package/types/api.d.ts +1 -1
  105. package/types/befly.d.ts +13 -12
  106. package/types/cache.d.ts +2 -2
  107. package/types/context.d.ts +1 -1
  108. package/types/database.d.ts +0 -5
  109. package/types/hook.d.ts +1 -10
  110. package/types/plugin.d.ts +2 -96
  111. package/types/sync.d.ts +19 -25
  112. package/utils/convertBigIntFields.ts +38 -0
  113. package/utils/disableMenusGlob.ts +85 -0
  114. package/utils/importDefault.ts +21 -0
  115. package/utils/isDirentDirectory.ts +23 -0
  116. package/utils/loadMenuConfigs.ts +145 -0
  117. package/utils/processFields.ts +25 -0
  118. package/utils/scanAddons.ts +72 -0
  119. package/utils/scanFiles.ts +129 -21
  120. package/utils/scanSources.ts +64 -0
  121. package/utils/sortModules.ts +137 -0
  122. package/checks/checkApp.ts +0 -55
  123. package/docs/cipher.md +0 -582
  124. package/docs/database.md +0 -1176
  125. package/hooks/rateLimit.ts +0 -276
  126. package/sync/syncAll.ts +0 -35
  127. package/sync/syncDb/apply.ts +0 -192
  128. package/sync/syncDb/constants.ts +0 -119
  129. package/sync/syncDb/ddl.ts +0 -251
  130. package/sync/syncDb/helpers.ts +0 -84
  131. package/sync/syncDb/schema.ts +0 -202
  132. package/sync/syncDb/sqlite.ts +0 -48
  133. package/sync/syncDb/table.ts +0 -207
  134. package/sync/syncDb/tableCreate.ts +0 -163
  135. package/sync/syncDb/types.ts +0 -132
  136. package/sync/syncDb/version.ts +0 -69
  137. package/sync/syncDb.ts +0 -168
  138. package/tests/rateLimit-hook.test.ts +0 -477
  139. package/tests/syncDb-constants.test.ts +0 -130
  140. package/tests/syncDb-schema.test.ts +0 -179
  141. package/tests/syncDb-types.test.ts +0 -139
  142. package/utils/addonHelper.ts +0 -90
  143. package/utils/modules.ts +0 -98
  144. package/utils/route.ts +0 -23
@@ -0,0 +1,137 @@
1
+ import { camelCase } from "es-toolkit/string";
2
+
3
+ import { Logger } from "../lib/logger.js";
4
+
5
+ export type SortModulesByDepsOptions<T> = {
6
+ /**
7
+ * 用于日志的模块标签(如:"插件"、"钩子")
8
+ */
9
+ moduleLabel?: string;
10
+
11
+ /**
12
+ * 生成模块名(用于 deps 解析与排序 key)。
13
+ * 默认:camelCase(item.fileName)
14
+ */
15
+ getName?: (item: T) => string;
16
+
17
+ /**
18
+ * 获取 deps。
19
+ * 默认:item.deps
20
+ */
21
+ getDeps?: (item: T) => string[];
22
+ };
23
+
24
+ /**
25
+ * 按 deps 拓扑排序 scanSources 扫描得到的插件/钩子。
26
+ *
27
+ * 说明:
28
+ * - 输入为 scanSources/scanFiles 的条目数组:每个条目包含 fileName 与 deps。
29
+ * - deps 里的字符串会与 getName(item) 的结果匹配。
30
+ * - 若出现:重复 name、缺失依赖、循环依赖,则返回 false。
31
+ */
32
+ export function sortModules<T extends { fileName: string; deps?: any }>(items: T[], options: SortModulesByDepsOptions<T> = {}): T[] | false {
33
+ const moduleLabel = options.moduleLabel || "模块";
34
+ const getName =
35
+ options.getName ||
36
+ ((item: T) => {
37
+ const moduleName = (item as any).moduleName;
38
+ if (typeof moduleName === "string" && moduleName.trim() !== "") {
39
+ return moduleName;
40
+ }
41
+ return camelCase(item.fileName);
42
+ });
43
+ const getDeps = options.getDeps || ((item: T) => (item as any).deps);
44
+
45
+ const result: T[] = [];
46
+ const visited = new Set<string>();
47
+ const visiting = new Set<string>();
48
+
49
+ const nameToItem: Record<string, T> = {};
50
+ let isPass = true;
51
+
52
+ // 1) 建表 + 重名检查
53
+ for (const item of items) {
54
+ const name = getName(item);
55
+
56
+ if (typeof name !== "string" || name.trim() === "") {
57
+ Logger.error({ item: item }, `${moduleLabel} 名称解析失败(getName 返回空字符串)`);
58
+ isPass = false;
59
+ continue;
60
+ }
61
+
62
+ if (nameToItem[name]) {
63
+ Logger.error(
64
+ {
65
+ name: name,
66
+ first: nameToItem[name],
67
+ second: item
68
+ },
69
+ `${moduleLabel} 名称重复,无法根据 deps 唯一定位`
70
+ );
71
+ isPass = false;
72
+ continue;
73
+ }
74
+
75
+ nameToItem[name] = item;
76
+ }
77
+
78
+ if (!isPass) return false;
79
+
80
+ // 2) 依赖存在性检查 + deps 类型检查
81
+ for (const item of items) {
82
+ const name = getName(item);
83
+ const deps = getDeps(item);
84
+
85
+ if (!Array.isArray(deps)) {
86
+ Logger.error({ module: name, item: item }, `${moduleLabel} 的 deps 必须是数组`);
87
+ isPass = false;
88
+ continue;
89
+ }
90
+
91
+ for (const dep of deps) {
92
+ if (typeof dep !== "string") {
93
+ Logger.error({ module: name, dependency: dep, item: item }, `${moduleLabel} 的 deps 必须是字符串数组`);
94
+ isPass = false;
95
+ continue;
96
+ }
97
+
98
+ if (!nameToItem[dep]) {
99
+ Logger.error({ module: name, dependency: dep }, `${moduleLabel} 依赖未找到`);
100
+ isPass = false;
101
+ }
102
+ }
103
+ }
104
+
105
+ if (!isPass) return false;
106
+
107
+ // 3) 拓扑排序(DFS)
108
+ const visit = (name: string): void => {
109
+ if (visited.has(name)) return;
110
+ if (visiting.has(name)) {
111
+ Logger.error({ module: name }, `${moduleLabel} 循环依赖`);
112
+ isPass = false;
113
+ return;
114
+ }
115
+
116
+ const item = nameToItem[name];
117
+ if (!item) return;
118
+
119
+ const deps = getDeps(item) as string[];
120
+
121
+ visiting.add(name);
122
+ for (const dep of deps) {
123
+ visit(dep);
124
+ }
125
+ visiting.delete(name);
126
+
127
+ visited.add(name);
128
+ result.push(item);
129
+ };
130
+
131
+ for (const item of items) {
132
+ const name = getName(item);
133
+ visit(name);
134
+ }
135
+
136
+ return isPass ? result : false;
137
+ }
@@ -1,55 +0,0 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
- // 内部依赖
3
- import { join } from "node:path";
4
-
5
- // 相对导入
6
- import { Logger } from "../lib/logger.js";
7
- import { projectApiDir, projectDir } from "../paths.js";
8
-
9
- /**
10
- * 检查项目结构
11
- */
12
- export async function checkApp(): Promise<void> {
13
- try {
14
- // 检查项目 apis 目录下是否存在名为 addon 的目录
15
- if (existsSync(projectApiDir)) {
16
- const addonDir = join(projectApiDir, "addon");
17
- if (existsSync(addonDir)) {
18
- throw new Error("项目 apis 目录下不能存在名为 addon 的目录,addon 是保留名称,用于组件接口路由");
19
- }
20
- }
21
-
22
- // 检查并创建 logs 目录
23
- const logsDir = join(projectDir, "logs");
24
- if (!existsSync(logsDir)) {
25
- mkdirSync(logsDir, { recursive: true });
26
- }
27
-
28
- // 检查并创建 configs 目录和配置文件
29
- const configsDir = join(projectDir, "configs");
30
- if (!existsSync(configsDir)) {
31
- mkdirSync(configsDir, { recursive: true });
32
- }
33
-
34
- // 检查并创建 befly.common.json
35
- const beflyJsonPath = join(configsDir, "befly.common.json");
36
- if (!existsSync(beflyJsonPath)) {
37
- writeFileSync(beflyJsonPath, "{}", "utf-8");
38
- }
39
-
40
- // 检查并创建 befly.development.json
41
- const beflyDevelopmentJsonPath = join(configsDir, "befly.development.json");
42
- if (!existsSync(beflyDevelopmentJsonPath)) {
43
- writeFileSync(beflyDevelopmentJsonPath, "{}", "utf-8");
44
- }
45
-
46
- // 检查并创建 befly.production.json
47
- const beflyProductionJsonPath = join(configsDir, "befly.production.json");
48
- if (!existsSync(beflyProductionJsonPath)) {
49
- writeFileSync(beflyProductionJsonPath, "{}", "utf-8");
50
- }
51
- } catch (error: any) {
52
- Logger.error("项目结构检查过程中出错", error);
53
- throw error;
54
- }
55
- }