befly 3.10.18 → 3.11.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 (223) hide show
  1. package/README.md +83 -307
  2. package/dist/befly.config.d.ts +7 -0
  3. package/{befly.config.ts → dist/befly.config.js} +11 -36
  4. package/dist/befly.js +15621 -0
  5. package/dist/befly.min.js +21 -0
  6. package/dist/checks/checkApi.d.ts +1 -0
  7. package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
  8. package/dist/checks/checkHook.d.ts +1 -0
  9. package/dist/checks/checkHook.js +86 -0
  10. package/dist/checks/checkMenu.d.ts +7 -0
  11. package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
  12. package/dist/checks/checkPlugin.d.ts +1 -0
  13. package/dist/checks/checkPlugin.js +86 -0
  14. package/dist/checks/checkTable.d.ts +6 -0
  15. package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
  16. package/dist/configs/presetFields.d.ts +4 -0
  17. package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
  18. package/dist/configs/presetRegexp.d.ts +145 -0
  19. package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
  20. package/dist/hooks/auth.d.ts +7 -0
  21. package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
  22. package/dist/hooks/cors.d.ts +11 -0
  23. package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
  24. package/dist/hooks/parser.d.ts +14 -0
  25. package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
  26. package/dist/hooks/permission.d.ts +14 -0
  27. package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
  28. package/dist/hooks/validator.d.ts +11 -0
  29. package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
  30. package/dist/index.d.ts +26 -0
  31. package/{main.ts → dist/index.js} +61 -100
  32. package/dist/lib/asyncContext.d.ts +21 -0
  33. package/dist/lib/asyncContext.js +27 -0
  34. package/dist/lib/cacheHelper.d.ts +95 -0
  35. package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
  36. package/dist/lib/cacheKeys.d.ts +23 -0
  37. package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
  38. package/dist/lib/cipher.d.ts +153 -0
  39. package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
  40. package/dist/lib/connect.d.ts +91 -0
  41. package/{lib/connect.ts → dist/lib/connect.js} +47 -88
  42. package/dist/lib/dbDialect.d.ts +87 -0
  43. package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
  44. package/dist/lib/dbHelper.d.ts +204 -0
  45. package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
  46. package/dist/lib/dbUtils.d.ts +68 -0
  47. package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
  48. package/dist/lib/jwt.d.ts +13 -0
  49. package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
  50. package/dist/lib/logger.d.ts +42 -0
  51. package/dist/lib/logger.js +1144 -0
  52. package/dist/lib/redisHelper.d.ts +185 -0
  53. package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
  54. package/dist/lib/sqlBuilder.d.ts +160 -0
  55. package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
  56. package/dist/lib/sqlCheck.d.ts +23 -0
  57. package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
  58. package/dist/lib/validator.d.ts +45 -0
  59. package/{lib/validator.ts → dist/lib/validator.js} +44 -61
  60. package/dist/loader/loadApis.d.ts +12 -0
  61. package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
  62. package/dist/loader/loadHooks.d.ts +7 -0
  63. package/dist/loader/loadHooks.js +35 -0
  64. package/dist/loader/loadPlugins.d.ts +8 -0
  65. package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
  66. package/dist/paths.d.ts +93 -0
  67. package/{paths.ts → dist/paths.js} +6 -19
  68. package/dist/plugins/cache.d.ts +16 -0
  69. package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
  70. package/dist/plugins/cipher.d.ts +12 -0
  71. package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
  72. package/dist/plugins/config.d.ts +12 -0
  73. package/dist/plugins/config.js +8 -0
  74. package/dist/plugins/db.d.ts +16 -0
  75. package/{plugins/db.ts → dist/plugins/db.js} +11 -17
  76. package/dist/plugins/jwt.d.ts +12 -0
  77. package/dist/plugins/jwt.js +12 -0
  78. package/dist/plugins/logger.d.ts +32 -0
  79. package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
  80. package/dist/plugins/redis.d.ts +16 -0
  81. package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
  82. package/dist/plugins/tool.d.ts +81 -0
  83. package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
  84. package/dist/router/api.d.ts +14 -0
  85. package/dist/router/api.js +107 -0
  86. package/dist/router/static.d.ts +9 -0
  87. package/{router/static.ts → dist/router/static.js} +20 -34
  88. package/dist/scripts/ensureDist.d.ts +1 -0
  89. package/dist/scripts/ensureDist.js +296 -0
  90. package/dist/sync/syncApi.d.ts +3 -0
  91. package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
  92. package/dist/sync/syncCache.d.ts +2 -0
  93. package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
  94. package/dist/sync/syncDev.d.ts +6 -0
  95. package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
  96. package/dist/sync/syncMenu.d.ts +14 -0
  97. package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
  98. package/dist/sync/syncTable.d.ts +151 -0
  99. package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
  100. package/{types → dist/types}/api.d.ts +12 -51
  101. package/dist/types/api.js +4 -0
  102. package/{types → dist/types}/befly.d.ts +32 -227
  103. package/dist/types/befly.js +4 -0
  104. package/{types → dist/types}/cache.d.ts +7 -15
  105. package/dist/types/cache.js +4 -0
  106. package/dist/types/cipher.d.ts +27 -0
  107. package/dist/types/cipher.js +7 -0
  108. package/{types → dist/types}/common.d.ts +8 -33
  109. package/dist/types/common.js +5 -0
  110. package/{types → dist/types}/context.d.ts +3 -5
  111. package/dist/types/context.js +4 -0
  112. package/{types → dist/types}/crypto.d.ts +0 -3
  113. package/dist/types/crypto.js +4 -0
  114. package/dist/types/database.d.ts +138 -0
  115. package/dist/types/database.js +4 -0
  116. package/dist/types/hook.d.ts +17 -0
  117. package/dist/types/hook.js +6 -0
  118. package/dist/types/jwt.d.ts +75 -0
  119. package/dist/types/jwt.js +4 -0
  120. package/dist/types/logger.d.ts +59 -0
  121. package/dist/types/logger.js +6 -0
  122. package/dist/types/plugin.d.ts +16 -0
  123. package/dist/types/plugin.js +6 -0
  124. package/dist/types/redis.d.ts +71 -0
  125. package/dist/types/redis.js +4 -0
  126. package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
  127. package/dist/types/roleApisCache.js +8 -0
  128. package/dist/types/sync.d.ts +92 -0
  129. package/dist/types/sync.js +4 -0
  130. package/dist/types/table.d.ts +34 -0
  131. package/dist/types/table.js +4 -0
  132. package/dist/types/validate.d.ts +67 -0
  133. package/dist/types/validate.js +4 -0
  134. package/dist/utils/calcPerfTime.d.ts +4 -0
  135. package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
  136. package/dist/utils/convertBigIntFields.d.ts +11 -0
  137. package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
  138. package/dist/utils/cors.d.ts +8 -0
  139. package/{utils/cors.ts → dist/utils/cors.js} +1 -3
  140. package/dist/utils/disableMenusGlob.d.ts +13 -0
  141. package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
  142. package/dist/utils/fieldClear.d.ts +11 -0
  143. package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
  144. package/dist/utils/getClientIp.d.ts +6 -0
  145. package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
  146. package/dist/utils/importDefault.d.ts +1 -0
  147. package/dist/utils/importDefault.js +29 -0
  148. package/dist/utils/isDirentDirectory.d.ts +2 -0
  149. package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
  150. package/dist/utils/loadMenuConfigs.d.ts +29 -0
  151. package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
  152. package/dist/utils/mergeAndConcat.d.ts +7 -0
  153. package/dist/utils/mergeAndConcat.js +72 -0
  154. package/dist/utils/processAtSymbol.d.ts +4 -0
  155. package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
  156. package/dist/utils/processInfo.d.ts +24 -0
  157. package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
  158. package/dist/utils/response.d.ts +20 -0
  159. package/{utils/response.ts → dist/utils/response.js} +28 -49
  160. package/dist/utils/scanAddons.d.ts +17 -0
  161. package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
  162. package/dist/utils/scanConfig.d.ts +26 -0
  163. package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
  164. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  165. package/dist/utils/scanCoreBuiltins.js +65 -0
  166. package/dist/utils/scanFiles.d.ts +30 -0
  167. package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
  168. package/dist/utils/scanSources.d.ts +10 -0
  169. package/dist/utils/scanSources.js +46 -0
  170. package/dist/utils/sortModules.d.ts +28 -0
  171. package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
  172. package/dist/utils/util.d.ts +84 -0
  173. package/dist/utils/util.js +262 -0
  174. package/package.json +26 -34
  175. package/.gitignore +0 -0
  176. package/bunfig.toml +0 -3
  177. package/checks/checkHook.ts +0 -48
  178. package/checks/checkPlugin.ts +0 -48
  179. package/configs/presetRegexp.ts +0 -225
  180. package/docs/README.md +0 -98
  181. package/docs/api/api.md +0 -1921
  182. package/docs/guide/examples.md +0 -926
  183. package/docs/guide/quickstart.md +0 -354
  184. package/docs/hooks/auth.md +0 -38
  185. package/docs/hooks/cors.md +0 -28
  186. package/docs/hooks/hook.md +0 -838
  187. package/docs/hooks/parser.md +0 -19
  188. package/docs/hooks/rateLimit.md +0 -47
  189. package/docs/infra/redis.md +0 -628
  190. package/docs/plugins/cipher.md +0 -61
  191. package/docs/plugins/database.md +0 -189
  192. package/docs/plugins/plugin.md +0 -986
  193. package/docs/reference/addon.md +0 -510
  194. package/docs/reference/config.md +0 -573
  195. package/docs/reference/logger.md +0 -495
  196. package/docs/reference/sync.md +0 -478
  197. package/docs/reference/table.md +0 -763
  198. package/docs/reference/validator.md +0 -620
  199. package/lib/asyncContext.ts +0 -43
  200. package/lib/logger.ts +0 -811
  201. package/loader/loadHooks.ts +0 -51
  202. package/plugins/config.ts +0 -13
  203. package/plugins/jwt.ts +0 -15
  204. package/router/api.ts +0 -130
  205. package/tsconfig.json +0 -8
  206. package/types/database.d.ts +0 -541
  207. package/types/hook.d.ts +0 -25
  208. package/types/jwt.d.ts +0 -118
  209. package/types/logger.d.ts +0 -65
  210. package/types/plugin.d.ts +0 -19
  211. package/types/redis.d.ts +0 -83
  212. package/types/sync.d.ts +0 -398
  213. package/types/table.d.ts +0 -216
  214. package/types/validate.d.ts +0 -69
  215. package/utils/arrayKeysToCamel.ts +0 -18
  216. package/utils/configTypes.ts +0 -3
  217. package/utils/genShortId.ts +0 -12
  218. package/utils/importDefault.ts +0 -21
  219. package/utils/keysToCamel.ts +0 -22
  220. package/utils/keysToSnake.ts +0 -22
  221. package/utils/pickFields.ts +0 -19
  222. package/utils/scanSources.ts +0 -64
  223. package/utils/sqlLog.ts +0 -37
@@ -0,0 +1,23 @@
1
+ /**
2
+ * SQL 入参校验工具(静态类)
3
+ *
4
+ * 目标:把“参数合法性/一致性/安全性”判断从 SqlBuilder 等拼接逻辑中拆出来,便于复用与维护。
5
+ *
6
+ * 说明:这里的校验仅关注“字符串/标识符/批量数据结构”层面的正确性;
7
+ * 具体 SQL 语义(如字段是否存在)不在此处校验。
8
+ */
9
+ export declare class SqlCheck {
10
+ private static readonly SAFE_IDENTIFIER_RE;
11
+ static assertNonEmptyString(value: unknown, label: string): asserts value is string;
12
+ static assertNoUndefinedParam(value: unknown, label: string): void;
13
+ static startsWithQuote(value: string): boolean;
14
+ static isQuotedIdentPaired(value: string): boolean;
15
+ static assertPairedQuotedIdentIfStartsWithQuote(value: string, label: string): void;
16
+ static assertSafeIdentifierPart(part: string, kind: "table" | "schema" | "alias" | "field"): void;
17
+ static assertSafeAlias(aliasPart: string): void;
18
+ static assertNoExprField(field: string): void;
19
+ static assertNoUndefinedInRecord(row: Record<string, unknown>, label: string): void;
20
+ static assertBatchInsertRowsConsistent(rows: Array<Record<string, unknown>>, options: {
21
+ table: string;
22
+ }): string[];
23
+ }
@@ -6,11 +6,9 @@
6
6
  * 说明:这里的校验仅关注“字符串/标识符/批量数据结构”层面的正确性;
7
7
  * 具体 SQL 语义(如字段是否存在)不在此处校验。
8
8
  */
9
-
10
9
  export class SqlCheck {
11
- private static readonly SAFE_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
12
-
13
- static assertNonEmptyString(value: unknown, label: string): asserts value is string {
10
+ static SAFE_IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
11
+ static assertNonEmptyString(value, label) {
14
12
  if (typeof value !== "string") {
15
13
  throw new Error(`${label} 必须是字符串 (value: ${String(value)})`);
16
14
  }
@@ -18,119 +16,104 @@ export class SqlCheck {
18
16
  throw new Error(`${label} 不能为空`);
19
17
  }
20
18
  }
21
-
22
- static assertNoUndefinedParam(value: unknown, label: string): void {
19
+ static assertNoUndefinedParam(value, label) {
23
20
  if (value === undefined) {
24
21
  throw new Error(`${label} 不能为 undefined`);
25
22
  }
26
23
  }
27
-
28
- static startsWithQuote(value: string): boolean {
24
+ static startsWithQuote(value) {
29
25
  const trimmed = value.trim();
30
26
  return trimmed.startsWith("`") || trimmed.startsWith('"');
31
27
  }
32
-
33
- static isQuotedIdentPaired(value: string): boolean {
28
+ static isQuotedIdentPaired(value) {
34
29
  const trimmed = value.trim();
35
- if (trimmed.length < 2) return false;
36
-
30
+ if (trimmed.length < 2)
31
+ return false;
37
32
  const first = trimmed[0];
38
33
  const last = trimmed[trimmed.length - 1];
39
-
40
- if (first === "`" && last === "`") return true;
41
- if (first === '"' && last === '"') return true;
42
-
34
+ if (first === "`" && last === "`")
35
+ return true;
36
+ if (first === '"' && last === '"')
37
+ return true;
43
38
  return false;
44
39
  }
45
-
46
- static assertPairedQuotedIdentIfStartsWithQuote(value: string, label: string): void {
40
+ static assertPairedQuotedIdentIfStartsWithQuote(value, label) {
47
41
  if (SqlCheck.startsWithQuote(value) && !SqlCheck.isQuotedIdentPaired(value)) {
48
42
  throw new Error(`${label} 引用不完整,请使用成对的 \`...\` 或 "..." (value: ${value})`);
49
43
  }
50
44
  }
51
-
52
- static assertSafeIdentifierPart(part: string, kind: "table" | "schema" | "alias" | "field"): void {
45
+ static assertSafeIdentifierPart(part, kind) {
53
46
  // 这里仅允许常规标识符(字母/数字/下划线),避免把复杂表达式混进“自动转义”路径。
54
47
  if (!SqlCheck.SAFE_IDENTIFIER_RE.test(part)) {
55
48
  throw new Error(`无效的 ${kind} 标识符: ${part}`);
56
49
  }
57
50
  }
58
-
59
- static assertSafeAlias(aliasPart: string): void {
51
+ static assertSafeAlias(aliasPart) {
60
52
  // alias 允许两种:
61
53
  // 1) 已经被引用(`alias` 或 "alias")
62
54
  // 2) 普通标识符(不允许带空格/符号),避免注入
63
- if (SqlCheck.isQuotedIdentPaired(aliasPart)) return;
55
+ if (SqlCheck.isQuotedIdentPaired(aliasPart))
56
+ return;
64
57
  if (!SqlCheck.SAFE_IDENTIFIER_RE.test(aliasPart)) {
65
58
  throw new Error(`无效的字段别名: ${aliasPart}`);
66
59
  }
67
60
  }
68
-
69
- static assertNoExprField(field: string): void {
70
- if (typeof field !== "string") return;
61
+ static assertNoExprField(field) {
62
+ if (typeof field !== "string")
63
+ return;
71
64
  const trimmed = field.trim();
72
- if (!trimmed) return;
73
-
65
+ if (!trimmed)
66
+ return;
74
67
  // 收紧:包含函数/表达式(括号)不允许走自动转义路径
75
68
  // 这类表达式应显式使用 selectRaw/whereRaw 以避免误拼接和注入风险
76
69
  if (trimmed.includes("(") || trimmed.includes(")")) {
77
70
  throw new Error(`字段包含函数/表达式,请使用 selectRaw/whereRaw (field: ${trimmed})`);
78
71
  }
79
72
  }
80
-
81
- static assertNoUndefinedInRecord(row: Record<string, unknown>, label: string): void {
73
+ static assertNoUndefinedInRecord(row, label) {
82
74
  for (const [key, value] of Object.entries(row)) {
83
75
  if (value === undefined) {
84
76
  throw new Error(`${label} 存在 undefined 字段值 (field: ${key})`);
85
77
  }
86
78
  }
87
79
  }
88
-
89
- static assertBatchInsertRowsConsistent(rows: Array<Record<string, unknown>>, options: { table: string }): string[] {
80
+ static assertBatchInsertRowsConsistent(rows, options) {
90
81
  if (!Array.isArray(rows)) {
91
82
  throw new Error("批量插入 rows 必须是数组");
92
83
  }
93
84
  if (rows.length === 0) {
94
85
  throw new Error(`插入数据不能为空 (table: ${options.table})`);
95
86
  }
96
-
97
87
  const first = rows[0];
98
88
  if (!first || typeof first !== "object" || Array.isArray(first)) {
99
89
  throw new Error(`批量插入的每一行必须是对象 (table: ${options.table}, rowIndex: 0)`);
100
90
  }
101
-
102
91
  const fields = Object.keys(first);
103
92
  if (fields.length === 0) {
104
93
  throw new Error(`插入数据必须至少有一个字段 (table: ${options.table})`);
105
94
  }
106
-
107
95
  const fieldSet = new Set(fields);
108
-
109
96
  for (let i = 0; i < rows.length; i++) {
110
97
  const row = rows[i];
111
98
  if (!row || typeof row !== "object" || Array.isArray(row)) {
112
99
  throw new Error(`批量插入的每一行必须是对象 (table: ${options.table}, rowIndex: ${i})`);
113
100
  }
114
-
115
101
  const rowKeys = Object.keys(row);
116
102
  if (rowKeys.length !== fields.length) {
117
103
  throw new Error(`批量插入每行字段必须一致 (table: ${options.table}, rowIndex: ${i})`);
118
104
  }
119
-
120
105
  for (const key of rowKeys) {
121
106
  if (!fieldSet.has(key)) {
122
107
  throw new Error(`批量插入每行字段必须一致 (table: ${options.table}, rowIndex: ${i}, extraField: ${key})`);
123
108
  }
124
109
  }
125
-
126
110
  for (const field of fields) {
127
111
  if (!(field in row)) {
128
112
  throw new Error(`批量插入缺少字段 (table: ${options.table}, rowIndex: ${i}, field: ${field})`);
129
113
  }
130
- SqlCheck.assertNoUndefinedParam((row as any)[field], `批量插入字段值 (table: ${options.table}, rowIndex: ${i}, field: ${field})`);
114
+ SqlCheck.assertNoUndefinedParam(row[field], `批量插入字段值 (table: ${options.table}, rowIndex: ${i}, field: ${field})`);
131
115
  }
132
116
  }
133
-
134
117
  return fields;
135
118
  }
136
119
  }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 数据验证器 - Befly 项目专用
3
+ * 纯静态类设计,简洁易用
4
+ */
5
+ import type { TableDefinition, FieldDefinition, ValidateResult, SingleResult } from "../types/validate";
6
+ /**
7
+ * 验证器类
8
+ *
9
+ * @example
10
+ * const result = Validator.validate(data, rules, ['email', 'name']);
11
+ * if (result.failed) {
12
+ * console.log(result.firstError);
13
+ * console.log(result.errors);
14
+ * console.log(result.errorFields);
15
+ * }
16
+ *
17
+ * const single = Validator.single(value, fieldDef);
18
+ * if (!single.error) {
19
+ * console.log(single.value);
20
+ * }
21
+ */
22
+ export declare class Validator {
23
+ /**
24
+ * 验证数据对象
25
+ */
26
+ static validate(data: Record<string, any>, rules: TableDefinition, required?: string[]): ValidateResult;
27
+ /**
28
+ * 验证单个值(带类型转换)
29
+ */
30
+ static single(value: any, fieldDef: FieldDefinition): SingleResult;
31
+ /** 构建结果对象 */
32
+ private static buildResult;
33
+ /** 验证单个字段 */
34
+ private static checkField;
35
+ /** 类型转换 */
36
+ private static convert;
37
+ /** 规则验证 */
38
+ private static checkRule;
39
+ /** 解析正则别名 */
40
+ private static resolveRegex;
41
+ /** 测试正则 */
42
+ private static testRegex;
43
+ /** 获取默认值 */
44
+ private static defaultFor;
45
+ }
@@ -2,11 +2,7 @@
2
2
  * 数据验证器 - Befly 项目专用
3
3
  * 纯静态类设计,简洁易用
4
4
  */
5
-
6
- import type { TableDefinition, FieldDefinition, ValidateResult, SingleResult } from "../types/validate.ts";
7
-
8
- import { RegexAliases, getCompiledRegex } from "../configs/presetRegexp.ts";
9
-
5
+ import { RegexAliases, getCompiledRegex } from "../configs/presetRegexp";
10
6
  /**
11
7
  * 验证器类
12
8
  *
@@ -27,9 +23,8 @@ export class Validator {
27
23
  /**
28
24
  * 验证数据对象
29
25
  */
30
- static validate(data: Record<string, any>, rules: TableDefinition, required: string[] = []): ValidateResult {
31
- const fieldErrors: Record<string, string> = {};
32
-
26
+ static validate(data, rules, required = []) {
27
+ const fieldErrors = {};
33
28
  // 参数检查
34
29
  if (!data || typeof data !== "object" || Array.isArray(data)) {
35
30
  return this.buildResult({ _error: "数据必须是对象格式" });
@@ -37,7 +32,6 @@ export class Validator {
37
32
  if (!rules || typeof rules !== "object") {
38
33
  return this.buildResult({ _error: "验证规则必须是对象格式" });
39
34
  }
40
-
41
35
  // 检查必填字段
42
36
  for (const field of required) {
43
37
  const value = data[field];
@@ -46,54 +40,46 @@ export class Validator {
46
40
  fieldErrors[field] = `${label}为必填项`;
47
41
  }
48
42
  }
49
-
50
43
  // 验证有值的字段
51
44
  for (const [field, rule] of Object.entries(rules)) {
52
- if (fieldErrors[field]) continue;
45
+ if (fieldErrors[field])
46
+ continue;
53
47
  // 字段值为 undefined 时跳过验证(除非是必填字段,但必填字段已在上面检查过)
54
- if (data[field] === undefined && !required.includes(field)) continue;
55
-
48
+ if (data[field] === undefined && !required.includes(field))
49
+ continue;
56
50
  const error = this.checkField(data[field], rule, field);
57
- if (error) fieldErrors[field] = error;
51
+ if (error)
52
+ fieldErrors[field] = error;
58
53
  }
59
-
60
54
  return this.buildResult(fieldErrors);
61
55
  }
62
-
63
56
  /**
64
57
  * 验证单个值(带类型转换)
65
58
  */
66
- static single(value: any, fieldDef: FieldDefinition): SingleResult {
59
+ static single(value, fieldDef) {
67
60
  const { type, default: defaultValue } = fieldDef;
68
-
69
61
  // 处理空值
70
62
  if (value === undefined || value === null || value === "") {
71
63
  return { value: this.defaultFor(type, defaultValue), error: null };
72
64
  }
73
-
74
65
  // 类型转换
75
66
  const converted = this.convert(value, type);
76
67
  if (converted.error) {
77
68
  return { value: null, error: converted.error };
78
69
  }
79
-
80
70
  // 规则验证
81
71
  const error = this.checkRule(converted.value, fieldDef);
82
72
  if (error) {
83
73
  return { value: null, error: error };
84
74
  }
85
-
86
75
  return { value: converted.value, error: null };
87
76
  }
88
-
89
77
  // ========== 私有方法 ==========
90
-
91
78
  /** 构建结果对象 */
92
- private static buildResult(fieldErrors: Record<string, string>): ValidateResult {
79
+ static buildResult(fieldErrors) {
93
80
  const errors = Object.values(fieldErrors);
94
81
  const errorFields = Object.keys(fieldErrors);
95
82
  const failed = errors.length > 0;
96
-
97
83
  return {
98
84
  code: failed ? 1 : 0,
99
85
  failed: failed,
@@ -103,22 +89,18 @@ export class Validator {
103
89
  fieldErrors: fieldErrors
104
90
  };
105
91
  }
106
-
107
92
  /** 验证单个字段 */
108
- private static checkField(value: any, fieldDef: FieldDefinition, fieldName: string): string | null {
93
+ static checkField(value, fieldDef, fieldName) {
109
94
  const label = fieldDef.name || fieldName;
110
-
111
95
  const converted = this.convert(value, fieldDef.type);
112
96
  if (converted.error) {
113
97
  return `${label}${converted.error}`;
114
98
  }
115
-
116
99
  const error = this.checkRule(converted.value, fieldDef);
117
100
  return error ? `${label}${error}` : null;
118
101
  }
119
-
120
102
  /** 类型转换 */
121
- private static convert(value: any, type: string): { value: any; error: string | null } {
103
+ static convert(value, type) {
122
104
  switch (type.toLowerCase()) {
123
105
  case "number":
124
106
  if (typeof value === "number") {
@@ -129,15 +111,12 @@ export class Validator {
129
111
  return Number.isNaN(num) || !isFinite(num) ? { value: null, error: "必须是数字" } : { value: num, error: null };
130
112
  }
131
113
  return { value: null, error: "必须是数字" };
132
-
133
114
  case "string":
134
115
  case "text":
135
116
  return typeof value === "string" ? { value: value, error: null } : { value: null, error: "必须是字符串" };
136
-
137
117
  case "array_string":
138
118
  case "array_text":
139
119
  return Array.isArray(value) ? { value: value, error: null } : { value: null, error: "必须是数组" };
140
-
141
120
  case "array_number_string":
142
121
  case "array_number_text":
143
122
  if (!Array.isArray(value)) {
@@ -150,77 +129,82 @@ export class Validator {
150
129
  }
151
130
  }
152
131
  return { value: value, error: null };
153
-
154
132
  default:
155
133
  return { value: value, error: null };
156
134
  }
157
135
  }
158
-
159
136
  /** 规则验证 */
160
- private static checkRule(value: any, fieldDef: FieldDefinition): string | null {
137
+ static checkRule(value, fieldDef) {
161
138
  const { type, min, max, regexp } = fieldDef;
162
139
  const regex = this.resolveRegex(regexp);
163
-
164
140
  switch (type.toLowerCase()) {
165
141
  case "number":
166
- if (min !== null && value < min) return `不能小于${min}`;
167
- if (max !== null && max > 0 && value > max) return `不能大于${max}`;
168
- if (regex && !this.testRegex(regex, String(value))) return "格式不正确";
142
+ if (min !== null && value < min)
143
+ return `不能小于${min}`;
144
+ if (max !== null && max > 0 && value > max)
145
+ return `不能大于${max}`;
146
+ if (regex && !this.testRegex(regex, String(value)))
147
+ return "格式不正确";
169
148
  break;
170
-
171
149
  case "string":
172
150
  case "text":
173
- if (min !== null && value.length < min) return `长度不能少于${min}个字符`;
174
- if (max !== null && max > 0 && value.length > max) return `长度不能超过${max}个字符`;
175
- if (regex && !this.testRegex(regex, value)) return "格式不正确";
151
+ if (min !== null && value.length < min)
152
+ return `长度不能少于${min}个字符`;
153
+ if (max !== null && max > 0 && value.length > max)
154
+ return `长度不能超过${max}个字符`;
155
+ if (regex && !this.testRegex(regex, value))
156
+ return "格式不正确";
176
157
  break;
177
-
178
158
  case "array_string":
179
159
  case "array_text":
180
160
  case "array_number_string":
181
161
  case "array_number_text":
182
- if (min !== null && value.length < min) return `至少需要${min}个元素`;
183
- if (max !== null && max > 0 && value.length > max) return `最多只能有${max}个元素`;
162
+ if (min !== null && value.length < min)
163
+ return `至少需要${min}个元素`;
164
+ if (max !== null && max > 0 && value.length > max)
165
+ return `最多只能有${max}个元素`;
184
166
  if (regex) {
185
167
  for (const item of value) {
186
- if (!this.testRegex(regex, String(item))) return "元素格式不正确";
168
+ if (!this.testRegex(regex, String(item)))
169
+ return "元素格式不正确";
187
170
  }
188
171
  }
189
172
  break;
190
173
  }
191
174
  return null;
192
175
  }
193
-
194
176
  /** 解析正则别名 */
195
- private static resolveRegex(regexp: string | null): string | null {
196
- if (!regexp) return null;
177
+ static resolveRegex(regexp) {
178
+ if (!regexp)
179
+ return null;
197
180
  if (regexp.startsWith("@")) {
198
- const key = regexp.substring(1) as keyof typeof RegexAliases;
181
+ const key = regexp.substring(1);
199
182
  return RegexAliases[key] || regexp;
200
183
  }
201
184
  return regexp;
202
185
  }
203
-
204
186
  /** 测试正则 */
205
- private static testRegex(pattern: string, value: string): boolean {
187
+ static testRegex(pattern, value) {
206
188
  try {
207
189
  return getCompiledRegex(pattern).test(value);
208
- } catch {
190
+ }
191
+ catch {
209
192
  return false;
210
193
  }
211
194
  }
212
-
213
195
  /** 获取默认值 */
214
- private static defaultFor(type: string, defaultValue: any): any {
196
+ static defaultFor(type, defaultValue) {
215
197
  // 如果字段定义了默认值,则使用字段默认值(优先级最高)
216
198
  if (defaultValue !== null && defaultValue !== undefined) {
217
199
  // 数组默认值
218
200
  if ((type === "array_string" || type === "array_text" || type === "array_number_string" || type === "array_number_text") && typeof defaultValue === "string") {
219
- if (defaultValue === "[]") return [];
201
+ if (defaultValue === "[]")
202
+ return [];
220
203
  try {
221
204
  const parsed = JSON.parse(defaultValue);
222
205
  return Array.isArray(parsed) ? parsed : [];
223
- } catch {
206
+ }
207
+ catch {
224
208
  return [];
225
209
  }
226
210
  }
@@ -231,7 +215,6 @@ export class Validator {
231
215
  }
232
216
  return defaultValue;
233
217
  }
234
-
235
218
  // 类型默认值(字段未定义 default 时使用)
236
219
  switch (type.toLowerCase()) {
237
220
  case "number":
@@ -0,0 +1,12 @@
1
+ /**
2
+ * API 加载器
3
+ * 负责扫描和加载所有 API 路由(组件、项目)
4
+ */
5
+ import type { ApiRoute } from "../types/api";
6
+ import type { ScanFileResult } from "../utils/scanFiles";
7
+ /**
8
+ * 加载所有 API 路由
9
+ * @param apiItems - scanSources/scanFiles 扫描到的 API 条目数组
10
+ * @returns API 路由映射表
11
+ */
12
+ export declare function loadApis(apis: ScanFileResult[]): Promise<Map<string, ApiRoute>>;
@@ -2,42 +2,32 @@
2
2
  * API 加载器
3
3
  * 负责扫描和加载所有 API 路由(组件、项目)
4
4
  */
5
-
6
- // 类型导入
7
- import type { ApiRoute } from "../types/api.ts";
8
- import type { ScanFileResult } from "../utils/scanFiles.ts";
9
-
10
- import { Logger } from "../lib/logger.ts";
11
- import { processFields } from "../utils/processFields.ts";
12
-
5
+ import { Logger } from "../lib/logger";
6
+ import { processAtSymbol } from "../utils/processAtSymbol";
13
7
  /**
14
8
  * 加载所有 API 路由
15
9
  * @param apiItems - scanSources/scanFiles 扫描到的 API 条目数组
16
10
  * @returns API 路由映射表
17
11
  */
18
- export async function loadApis(apis: ScanFileResult[]): Promise<Map<string, ApiRoute>> {
19
- const apisMap = new Map<string, ApiRoute>();
20
-
12
+ export async function loadApis(apis) {
13
+ const apisMap = new Map();
21
14
  for (const api of apis) {
22
- const apiType = (api as any).type;
15
+ const apiType = api.type;
23
16
  // 兼容:scanFiles 的结果或测试构造数据可能缺少 type 字段;缺少时默认按 API 处理。
24
17
  // 仅在 type 显式存在且不等于 "api" 时跳过,避免错误过滤。
25
18
  if (apiType && apiType !== "api") {
26
19
  continue;
27
20
  }
28
-
29
21
  try {
30
- const apiRoute = api as any;
31
-
22
+ const apiRoute = api;
32
23
  // 处理字段定义,将 @ 引用替换为实际字段定义
33
- apiRoute.fields = processFields(apiRoute.fields || {}, apiRoute.name, apiRoute.routePath);
34
-
35
- apisMap.set(apiRoute.routePath, apiRoute as ApiRoute);
36
- } catch (error: any) {
24
+ apiRoute.fields = processAtSymbol(apiRoute.fields || {}, apiRoute.name, apiRoute.routePath);
25
+ apisMap.set(apiRoute.routePath, apiRoute);
26
+ }
27
+ catch (error) {
37
28
  Logger.error({ err: error, api: api.relativePath, file: api.filePath }, "接口加载失败");
38
29
  throw error;
39
30
  }
40
31
  }
41
-
42
32
  return apisMap;
43
33
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 钩子加载器
3
+ * 默认加载所有来源钩子(core/addon/app)
4
+ */
5
+ import type { Hook } from "../types/hook";
6
+ import type { ScanFileResult } from "../utils/scanFiles";
7
+ export declare function loadHooks(hooks: ScanFileResult[]): Promise<Hook[]>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 钩子加载器
3
+ * 默认加载所有来源钩子(core/addon/app)
4
+ */
5
+ import { sortModules } from "../utils/sortModules";
6
+ export async function loadHooks(hooks) {
7
+ const hooksMap = [];
8
+ const enabledHooks = hooks.filter((item) => {
9
+ const moduleName = item?.moduleName;
10
+ if (typeof moduleName !== "string" || moduleName.trim() === "") {
11
+ return false;
12
+ }
13
+ // enable=false 表示禁用(替代 disableHooks 列表)。
14
+ // enable 仅允许 boolean;缺失 enable 的默认值应在 checkHook 阶段被补全为 true。
15
+ if (item?.enable === false) {
16
+ return false;
17
+ }
18
+ return true;
19
+ });
20
+ const sortedHooks = sortModules(enabledHooks, { moduleLabel: "钩子" });
21
+ if (sortedHooks === false) {
22
+ throw new Error("钩子依赖关系错误");
23
+ }
24
+ for (const item of sortedHooks) {
25
+ const hookName = item.moduleName;
26
+ const hook = item;
27
+ hooksMap.push({
28
+ name: hookName,
29
+ enable: true,
30
+ deps: hook.deps,
31
+ handler: hook.handler
32
+ });
33
+ }
34
+ return hooksMap;
35
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 插件加载器
3
+ * 负责扫描和初始化所有插件(核心、组件、项目)
4
+ */
5
+ import type { BeflyContext } from "../types/befly";
6
+ import type { Plugin } from "../types/plugin";
7
+ import type { ScanFileResult } from "../utils/scanFiles";
8
+ export declare function loadPlugins(plugins: ScanFileResult[], context: BeflyContext): Promise<Plugin[]>;
@@ -2,55 +2,43 @@
2
2
  * 插件加载器
3
3
  * 负责扫描和初始化所有插件(核心、组件、项目)
4
4
  */
5
-
6
- import type { BeflyContext } from "../types/befly.ts";
7
- import type { Plugin } from "../types/plugin.ts";
8
- import type { ScanFileResult } from "../utils/scanFiles.ts";
9
-
10
- import { Logger } from "../lib/logger.ts";
11
- import { sortModules } from "../utils/sortModules.ts";
12
-
13
- export async function loadPlugins(plugins: ScanFileResult[], context: BeflyContext, disablePlugins: string[] = []): Promise<Plugin[]> {
14
- const pluginsMap: Plugin[] = [];
15
-
16
- if (disablePlugins.length > 0) {
17
- Logger.info({ plugins: disablePlugins }, "禁用插件");
18
- }
19
-
20
- const enabledPlugins = plugins.filter((item: any) => {
5
+ import { Logger } from "../lib/logger";
6
+ import { sortModules } from "../utils/sortModules";
7
+ export async function loadPlugins(plugins, context) {
8
+ const pluginsMap = [];
9
+ const enabledPlugins = plugins.filter((item) => {
21
10
  const moduleName = item?.moduleName;
22
11
  if (typeof moduleName !== "string" || moduleName.trim() === "") {
23
12
  return false;
24
13
  }
25
- if (disablePlugins.includes(moduleName)) {
14
+ // enable=false 表示禁用(替代 disablePlugins 列表)。
15
+ // enable 仅允许 boolean;缺失 enable 的默认值应在 checkPlugin 阶段被补全为 true。
16
+ if (item?.enable === false) {
26
17
  return false;
27
18
  }
28
19
  return true;
29
20
  });
30
-
31
21
  const sortedPlugins = sortModules(enabledPlugins, { moduleLabel: "插件" });
32
22
  if (sortedPlugins === false) {
33
23
  throw new Error("插件依赖关系错误");
34
24
  }
35
-
36
25
  for (const item of sortedPlugins) {
37
- const pluginName = (item as any).moduleName as string;
38
- const plugin = item as any as Plugin;
39
-
26
+ const pluginName = item.moduleName;
27
+ const plugin = item;
40
28
  try {
41
29
  const pluginInstance = typeof plugin.handler === "function" ? await plugin.handler(context) : {};
42
- (context as any)[pluginName] = pluginInstance;
43
-
30
+ context[pluginName] = pluginInstance;
44
31
  pluginsMap.push({
45
32
  name: pluginName,
33
+ enable: true,
46
34
  deps: plugin.deps,
47
35
  handler: plugin.handler
48
36
  });
49
- } catch (error: any) {
37
+ }
38
+ catch (error) {
50
39
  Logger.error({ err: error, plugin: pluginName }, "插件初始化失败");
51
40
  throw error;
52
41
  }
53
42
  }
54
-
55
43
  return pluginsMap;
56
44
  }