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
@@ -1,26 +1,5 @@
1
- import { camelCase } from "es-toolkit/string";
2
-
3
- import { Logger } from "../lib/logger.ts";
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
-
1
+ import { Logger } from "../lib/logger";
2
+ import { camelCase } from "./util";
24
3
  /**
25
4
  * 按 deps 拓扑排序 scanSources 扫描得到的插件/钩子。
26
5
  *
@@ -29,109 +8,90 @@ export type SortModulesByDepsOptions<T> = {
29
8
  * - deps 里的字符串会与 getName(item) 的结果匹配。
30
9
  * - 若出现:重复 name、缺失依赖、循环依赖,则返回 false。
31
10
  */
32
- export function sortModules<T extends { fileName: string; deps?: any }>(items: T[], options: SortModulesByDepsOptions<T> = {}): T[] | false {
11
+ export function sortModules(items, options = {}) {
33
12
  const moduleLabel = options.moduleLabel || "模块";
34
- const getName =
35
- options.getName ||
36
- ((item: T) => {
37
- const moduleName = (item as any).moduleName;
13
+ const getName = options.getName ||
14
+ ((item) => {
15
+ const moduleName = item.moduleName;
38
16
  if (typeof moduleName === "string" && moduleName.trim() !== "") {
39
17
  return moduleName;
40
18
  }
41
19
  return camelCase(item.fileName);
42
20
  });
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> = {};
21
+ const getDeps = options.getDeps || ((item) => item.deps);
22
+ const result = [];
23
+ const visited = new Set();
24
+ const visiting = new Set();
25
+ const nameToItem = {};
50
26
  let isPass = true;
51
-
52
27
  // 1) 建表 + 重名检查
53
28
  for (const item of items) {
54
29
  const name = getName(item);
55
-
56
30
  if (typeof name !== "string" || name.trim() === "") {
57
31
  Logger.error({ item: item }, `${moduleLabel} 名称解析失败(getName 返回空字符串)`);
58
32
  isPass = false;
59
33
  continue;
60
34
  }
61
-
62
35
  if (nameToItem[name]) {
63
- Logger.error(
64
- {
65
- name: name,
66
- first: nameToItem[name],
67
- second: item
68
- },
69
- `${moduleLabel} 名称重复,无法根据 deps 唯一定位`
70
- );
36
+ Logger.error({
37
+ name: name,
38
+ first: nameToItem[name],
39
+ second: item
40
+ }, `${moduleLabel} 名称重复,无法根据 deps 唯一定位`);
71
41
  isPass = false;
72
42
  continue;
73
43
  }
74
-
75
44
  nameToItem[name] = item;
76
45
  }
77
-
78
- if (!isPass) return false;
79
-
46
+ if (!isPass)
47
+ return false;
80
48
  // 2) 依赖存在性检查 + deps 类型检查
81
49
  for (const item of items) {
82
50
  const name = getName(item);
83
51
  const deps = getDeps(item);
84
-
85
52
  if (!Array.isArray(deps)) {
86
53
  Logger.error({ module: name, item: item }, `${moduleLabel} 的 deps 必须是数组`);
87
54
  isPass = false;
88
55
  continue;
89
56
  }
90
-
91
57
  for (const dep of deps) {
92
58
  if (typeof dep !== "string") {
93
59
  Logger.error({ module: name, dependency: dep, item: item }, `${moduleLabel} 的 deps 必须是字符串数组`);
94
60
  isPass = false;
95
61
  continue;
96
62
  }
97
-
98
63
  if (!nameToItem[dep]) {
99
64
  Logger.error({ module: name, dependency: dep }, `${moduleLabel} 依赖未找到`);
100
65
  isPass = false;
101
66
  }
102
67
  }
103
68
  }
104
-
105
- if (!isPass) return false;
106
-
69
+ if (!isPass)
70
+ return false;
107
71
  // 3) 拓扑排序(DFS)
108
- const visit = (name: string): void => {
109
- if (visited.has(name)) return;
72
+ const visit = (name) => {
73
+ if (visited.has(name))
74
+ return;
110
75
  if (visiting.has(name)) {
111
76
  Logger.error({ module: name }, `${moduleLabel} 循环依赖`);
112
77
  isPass = false;
113
78
  return;
114
79
  }
115
-
116
80
  const item = nameToItem[name];
117
- if (!item) return;
118
-
119
- const deps = getDeps(item) as string[];
120
-
81
+ if (!item)
82
+ return;
83
+ const deps = getDeps(item);
121
84
  visiting.add(name);
122
85
  for (const dep of deps) {
123
86
  visit(dep);
124
87
  }
125
88
  visiting.delete(name);
126
-
127
89
  visited.add(name);
128
90
  result.push(item);
129
91
  };
130
-
131
92
  for (const item of items) {
132
93
  const name = getName(item);
133
94
  visit(name);
134
95
  }
135
-
136
96
  return isPass ? result : false;
137
97
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 注意:本文件用于集中维护 core 内部通用 utils。
3
+ * - 按项目规范:避免零散小文件噪音;实现集中在本文件,而不是做 re-export 聚合。
4
+ * - 仅在 packages/core 内部使用;core 对外不承诺这些路径导出。
5
+ */
6
+ export declare function isPlainObject(value: unknown): value is Record<string, any>;
7
+ /**
8
+ * 激进空值判断(项目约定):
9
+ * - null/undefined => empty
10
+ * - "" / 全空白字符串 => empty
11
+ * - 0 / NaN => empty
12
+ * - false => empty
13
+ * - Array => length === 0
14
+ * - Map/Set => size === 0
15
+ * - plain object => 无自有 key
16
+ * - 其他类型 => false
17
+ */
18
+ export declare function isEmpty(value: unknown): boolean;
19
+ export declare function forOwn(obj: unknown, iteratee: (value: any, key: string) => void): void;
20
+ /**
21
+ * 把字符串转为小驼峰。
22
+ * - 主要用于文件名/目录名(例如 my_plugin / my-plugin / my plugin)。
23
+ */
24
+ export declare function camelCase(input: string): string;
25
+ /**
26
+ * 把字符串转为 snake_case。
27
+ * - 主要用于表名/字段名(例如 userId -> user_id)。
28
+ */
29
+ export declare function snakeCase(input: string): string;
30
+ /**
31
+ * 对象字段名转小驼峰
32
+ * @param obj - 源对象
33
+ * @returns 字段名转为小驼峰格式的新对象
34
+ *
35
+ * @example
36
+ * keysToCamel({ user_id: 123, user_name: 'John' }) // { userId: 123, userName: 'John' }
37
+ * keysToCamel({ created_at: 1697452800000 }) // { createdAt: 1697452800000 }
38
+ */
39
+ export declare const keysToCamel: <T = any>(obj: Record<string, any>) => T;
40
+ /**
41
+ * 对象字段名转下划线
42
+ * @param obj - 源对象
43
+ * @returns 字段名转为下划线格式的新对象
44
+ *
45
+ * @example
46
+ * keysToSnake({ userId: 123, userName: 'John' }) // { user_id: 123, user_name: 'John' }
47
+ * keysToSnake({ createdAt: 1697452800000 }) // { created_at: 1697452800000 }
48
+ */
49
+ export declare const keysToSnake: <T = any>(obj: Record<string, any>) => T;
50
+ /**
51
+ * 数组对象字段名批量转小驼峰
52
+ * @param arr - 源数组
53
+ * @returns 字段名转为小驼峰格式的新数组
54
+ *
55
+ * @example
56
+ * arrayKeysToCamel([
57
+ * { user_id: 1, user_name: 'John' },
58
+ * { user_id: 2, user_name: 'Jane' }
59
+ * ])
60
+ * // [{ userId: 1, userName: 'John' }, { userId: 2, userName: 'Jane' }]
61
+ */
62
+ export declare const arrayKeysToCamel: <T = any>(arr: Record<string, any>[]) => T[];
63
+ export declare function getByPath(obj: unknown, path: string): unknown;
64
+ export declare function setByPath(target: Record<string, any>, path: string, value: unknown): void;
65
+ /**
66
+ * 返回一个移除指定 key 的浅拷贝。
67
+ * - 仅处理 plain object;其他类型返回空对象,避免日志场景抛错。
68
+ */
69
+ export declare function omit<T extends Record<string, any>>(obj: unknown, keys: string[]): Partial<T>;
70
+ /**
71
+ * 挑选指定字段
72
+ */
73
+ export declare const pickFields: <T extends Record<string, any>>(obj: T, keys: string[]) => Partial<T>;
74
+ export declare function keyBy<T>(items: T[], getKey: (item: T) => string): Record<string, T>;
75
+ /**
76
+ * 生成短 ID
77
+ * 由时间戳(base36)+ 随机字符组成,约 13 位
78
+ * - 前 8 位:时间戳(可排序)
79
+ * - 后 5 位:随机字符(防冲突)
80
+ * @returns 短 ID 字符串
81
+ * @example
82
+ * genShortId() // "lxyz1a2b3c4"
83
+ */
84
+ export declare function genShortId(): string;
@@ -0,0 +1,262 @@
1
+ /**
2
+ * 注意:本文件用于集中维护 core 内部通用 utils。
3
+ * - 按项目规范:避免零散小文件噪音;实现集中在本文件,而不是做 re-export 聚合。
4
+ * - 仅在 packages/core 内部使用;core 对外不承诺这些路径导出。
5
+ */
6
+ export function isPlainObject(value) {
7
+ if (typeof value !== "object" || value === null) {
8
+ return false;
9
+ }
10
+ const proto = Object.getPrototypeOf(value);
11
+ return proto === Object.prototype || proto === null;
12
+ }
13
+ /**
14
+ * 激进空值判断(项目约定):
15
+ * - null/undefined => empty
16
+ * - "" / 全空白字符串 => empty
17
+ * - 0 / NaN => empty
18
+ * - false => empty
19
+ * - Array => length === 0
20
+ * - Map/Set => size === 0
21
+ * - plain object => 无自有 key
22
+ * - 其他类型 => false
23
+ */
24
+ export function isEmpty(value) {
25
+ if (value === null || value === undefined) {
26
+ return true;
27
+ }
28
+ if (typeof value === "string") {
29
+ return value.trim().length === 0;
30
+ }
31
+ if (typeof value === "number") {
32
+ return value === 0 || Number.isNaN(value);
33
+ }
34
+ if (typeof value === "boolean") {
35
+ return value === false;
36
+ }
37
+ if (Array.isArray(value)) {
38
+ return value.length === 0;
39
+ }
40
+ if (value instanceof Map || value instanceof Set) {
41
+ return value.size === 0;
42
+ }
43
+ if (isPlainObject(value)) {
44
+ return Object.keys(value).length === 0;
45
+ }
46
+ return false;
47
+ }
48
+ export function forOwn(obj, iteratee) {
49
+ if (typeof iteratee !== "function") {
50
+ return;
51
+ }
52
+ if (!isPlainObject(obj)) {
53
+ return;
54
+ }
55
+ for (const key of Object.keys(obj)) {
56
+ iteratee(obj[key], key);
57
+ }
58
+ }
59
+ function toWordParts(input) {
60
+ const normalized = String(input)
61
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
62
+ .replace(/[^a-zA-Z0-9]+/g, " ")
63
+ .trim();
64
+ if (normalized.length === 0) {
65
+ return [];
66
+ }
67
+ return normalized.split(/\s+/).filter((p) => p.length > 0);
68
+ }
69
+ function upperFirst(s) {
70
+ if (s.length === 0) {
71
+ return s;
72
+ }
73
+ return s[0].toUpperCase() + s.slice(1);
74
+ }
75
+ /**
76
+ * 把字符串转为小驼峰。
77
+ * - 主要用于文件名/目录名(例如 my_plugin / my-plugin / my plugin)。
78
+ */
79
+ export function camelCase(input) {
80
+ const parts = toWordParts(input);
81
+ if (parts.length === 0) {
82
+ return "";
83
+ }
84
+ const first = parts[0].toLowerCase();
85
+ const rest = parts.slice(1).map((p) => upperFirst(p.toLowerCase()));
86
+ return [first, ...rest].join("");
87
+ }
88
+ function normalizeToWords(input) {
89
+ return String(input)
90
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
91
+ .replace(/[^a-zA-Z0-9]+/g, " ")
92
+ .trim();
93
+ }
94
+ /**
95
+ * 把字符串转为 snake_case。
96
+ * - 主要用于表名/字段名(例如 userId -> user_id)。
97
+ */
98
+ export function snakeCase(input) {
99
+ const normalized = normalizeToWords(input);
100
+ if (normalized.length === 0) {
101
+ return "";
102
+ }
103
+ return normalized
104
+ .split(/\s+/)
105
+ .filter((p) => p.length > 0)
106
+ .map((p) => p.toLowerCase())
107
+ .join("_");
108
+ }
109
+ /**
110
+ * 对象字段名转小驼峰
111
+ * @param obj - 源对象
112
+ * @returns 字段名转为小驼峰格式的新对象
113
+ *
114
+ * @example
115
+ * keysToCamel({ user_id: 123, user_name: 'John' }) // { userId: 123, userName: 'John' }
116
+ * keysToCamel({ created_at: 1697452800000 }) // { createdAt: 1697452800000 }
117
+ */
118
+ export const keysToCamel = (obj) => {
119
+ if (!obj || !isPlainObject(obj))
120
+ return obj;
121
+ const result = {};
122
+ for (const [key, value] of Object.entries(obj)) {
123
+ const camelKey = camelCase(key);
124
+ result[camelKey] = value;
125
+ }
126
+ return result;
127
+ };
128
+ /**
129
+ * 对象字段名转下划线
130
+ * @param obj - 源对象
131
+ * @returns 字段名转为下划线格式的新对象
132
+ *
133
+ * @example
134
+ * keysToSnake({ userId: 123, userName: 'John' }) // { user_id: 123, user_name: 'John' }
135
+ * keysToSnake({ createdAt: 1697452800000 }) // { created_at: 1697452800000 }
136
+ */
137
+ export const keysToSnake = (obj) => {
138
+ if (!obj || !isPlainObject(obj))
139
+ return obj;
140
+ const result = {};
141
+ for (const [key, value] of Object.entries(obj)) {
142
+ const snakeKey = snakeCase(key);
143
+ result[snakeKey] = value;
144
+ }
145
+ return result;
146
+ };
147
+ /**
148
+ * 数组对象字段名批量转小驼峰
149
+ * @param arr - 源数组
150
+ * @returns 字段名转为小驼峰格式的新数组
151
+ *
152
+ * @example
153
+ * arrayKeysToCamel([
154
+ * { user_id: 1, user_name: 'John' },
155
+ * { user_id: 2, user_name: 'Jane' }
156
+ * ])
157
+ * // [{ userId: 1, userName: 'John' }, { userId: 2, userName: 'Jane' }]
158
+ */
159
+ export const arrayKeysToCamel = (arr) => {
160
+ if (!arr || !Array.isArray(arr))
161
+ return arr;
162
+ return arr.map((item) => keysToCamel(item));
163
+ };
164
+ export function getByPath(obj, path) {
165
+ if (!path) {
166
+ return obj;
167
+ }
168
+ const parts = path.split(".");
169
+ let cur = obj;
170
+ for (const part of parts) {
171
+ if (cur === null || cur === undefined) {
172
+ return undefined;
173
+ }
174
+ if (typeof cur !== "object") {
175
+ return undefined;
176
+ }
177
+ cur = cur[part];
178
+ }
179
+ return cur;
180
+ }
181
+ export function setByPath(target, path, value) {
182
+ const parts = path.split(".");
183
+ // 避免无效 path(如 a..b)导致部分写入
184
+ for (const part of parts) {
185
+ if (!part) {
186
+ return;
187
+ }
188
+ }
189
+ let cur = target;
190
+ for (let i = 0; i < parts.length; i++) {
191
+ const key = parts[i];
192
+ const isLast = i === parts.length - 1;
193
+ if (isLast) {
194
+ cur[key] = value;
195
+ return;
196
+ }
197
+ const nextVal = cur[key];
198
+ if (!isPlainObject(nextVal)) {
199
+ cur[key] = {};
200
+ }
201
+ cur = cur[key];
202
+ }
203
+ }
204
+ /**
205
+ * 返回一个移除指定 key 的浅拷贝。
206
+ * - 仅处理 plain object;其他类型返回空对象,避免日志场景抛错。
207
+ */
208
+ export function omit(obj, keys) {
209
+ if (!isPlainObject(obj)) {
210
+ return {};
211
+ }
212
+ const keySet = new Set(Array.isArray(keys) ? keys : []);
213
+ const out = {};
214
+ for (const [k, v] of Object.entries(obj)) {
215
+ if (keySet.has(k)) {
216
+ continue;
217
+ }
218
+ out[k] = v;
219
+ }
220
+ return out;
221
+ }
222
+ /**
223
+ * 挑选指定字段
224
+ */
225
+ export const pickFields = (obj, keys) => {
226
+ if (!obj || (!isPlainObject(obj) && !Array.isArray(obj))) {
227
+ return {};
228
+ }
229
+ const result = {};
230
+ for (const key of keys) {
231
+ if (key in obj) {
232
+ result[key] = obj[key];
233
+ }
234
+ }
235
+ return result;
236
+ };
237
+ export function keyBy(items, getKey) {
238
+ const out = {};
239
+ if (!Array.isArray(items) || typeof getKey !== "function") {
240
+ return out;
241
+ }
242
+ for (const item of items) {
243
+ const key = getKey(item);
244
+ if (typeof key !== "string" || key === "") {
245
+ continue;
246
+ }
247
+ out[key] = item;
248
+ }
249
+ return out;
250
+ }
251
+ /**
252
+ * 生成短 ID
253
+ * 由时间戳(base36)+ 随机字符组成,约 13 位
254
+ * - 前 8 位:时间戳(可排序)
255
+ * - 后 5 位:随机字符(防冲突)
256
+ * @returns 短 ID 字符串
257
+ * @example
258
+ * genShortId() // "lxyz1a2b3c4"
259
+ */
260
+ export function genShortId() {
261
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
262
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.10.18",
4
- "gitHead": "1fb1ccce3a69fb6a57f42a1e269c307ed159ae9a",
3
+ "version": "3.11.1",
4
+ "gitHead": "b566edda48fcd5e8e7c66d001fefedc68520036a",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
7
7
  "keywords": [
@@ -20,57 +20,49 @@
20
20
  "license": "Apache-2.0",
21
21
  "author": "chensuiyi <bimostyle@qq.com>",
22
22
  "files": [
23
- ".gitignore",
24
- "befly.config.ts",
25
- "bunfig.toml",
26
23
  "LICENSE",
27
- "main.ts",
28
- "package.json",
29
- "paths.ts",
30
24
  "README.md",
31
- "tsconfig.json",
32
- "checks/",
33
- "configs/",
34
- "docs/",
35
- "hooks/",
36
- "lib/",
37
- "loader/",
38
- "plugins/",
39
- "router/",
40
- "sync/",
41
- "types/",
42
- "utils/"
25
+ "dist/"
43
26
  ],
44
27
  "type": "module",
45
- "main": "main.ts",
46
- "types": "main.ts",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
47
30
  "exports": {
48
31
  ".": {
49
- "types": "./main.ts",
50
- "default": "./main.ts"
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
51
34
  },
52
- "./lib/*": "./lib/*.ts",
53
- "./types/*": "./types/*.d.ts",
54
- "./utils/*": "./utils/*.ts"
35
+ "./all": {
36
+ "types": "./dist/index.d.ts",
37
+ "import": "./dist/befly.js",
38
+ "default": "./dist/befly.js"
39
+ },
40
+ "./min": {
41
+ "types": "./dist/index.d.ts",
42
+ "import": "./dist/befly.min.js",
43
+ "default": "./dist/befly.min.js"
44
+ },
45
+ "./types/*": {
46
+ "types": "./dist/types/*.d.ts"
47
+ }
55
48
  },
56
49
  "publishConfig": {
57
50
  "access": "public",
58
51
  "registry": "https://registry.npmjs.org"
59
52
  },
60
53
  "scripts": {
54
+ "clean": "rimraf dist",
61
55
  "test": "bun test",
56
+ "bundle": "bun build ./index.ts --outfile ./dist/befly.js --target bun --format esm --packages bundle",
57
+ "bundle:min": "bun build ./index.ts --outdir ./dist --entry-naming befly.min.js --target bun --format esm --packages bundle --minify",
58
+ "build": "rimraf dist && bunx tsgo -p tsconfig.build.json && bun run bundle && bun run bundle:min",
59
+ "prepack": "bun run build && bun run ./scripts/ensureDist.ts",
62
60
  "typecheck": "bunx tsgo -p tsconfig.json --noEmit"
63
61
  },
64
62
  "dependencies": {
65
- "befly-shared": "^1.3.10",
66
- "chalk": "^5.6.2",
67
- "es-toolkit": "^1.43.0",
68
63
  "fast-jwt": "^6.1.0",
69
64
  "fast-xml-parser": "^5.3.3",
70
- "merge-anything": "^6.0.6",
71
- "pathe": "^2.0.3",
72
- "pino": "^10.1.0",
73
- "pino-roll": "^4.0.0"
65
+ "pathe": "^2.0.3"
74
66
  },
75
67
  "engines": {
76
68
  "bun": ">=1.3.0"
package/.gitignore DELETED
File without changes
package/bunfig.toml DELETED
@@ -1,3 +0,0 @@
1
- env = false
2
- [install]
3
- linker = "isolated"
@@ -1,48 +0,0 @@
1
- import { isPlainObject } from "es-toolkit/compat";
2
- import { omit } from "es-toolkit/object";
3
-
4
- import { Logger } from "../lib/logger.ts";
5
-
6
- export async function checkHook(hooks: any[]): Promise<void> {
7
- let hasError = false;
8
-
9
- for (const hook of hooks) {
10
- try {
11
- if (!isPlainObject(hook)) {
12
- Logger.warn(omit(hook, ["handler"]), "钩子导出必须是对象(export default { deps, handler })");
13
- hasError = true;
14
- continue;
15
- }
16
-
17
- if (!Array.isArray((hook as any).deps)) {
18
- Logger.warn(omit(hook, ["handler"]), "钩子的 deps 属性必须是字符串数组");
19
- hasError = true;
20
- continue;
21
- }
22
-
23
- if ((hook as any).deps.some((depItem: any) => typeof depItem !== "string")) {
24
- Logger.warn(omit(hook, ["handler"]), "钩子的 deps 属性必须是字符串数组");
25
- hasError = true;
26
- }
27
-
28
- if (typeof (hook as any).handler !== "function") {
29
- Logger.warn(omit(hook, ["handler"]), "钩子的 handler 属性必须是函数");
30
- hasError = true;
31
- continue;
32
- }
33
- } catch (error: any) {
34
- Logger.error(
35
- {
36
- err: error,
37
- item: hook
38
- },
39
- "钩子解析失败"
40
- );
41
- hasError = true;
42
- }
43
- }
44
-
45
- if (hasError) {
46
- throw new Error("钩子结构检查失败");
47
- }
48
- }