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,49 +1,25 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
1
9
  import { existsSync } from "node:fs";
2
10
  import { isAbsolute, join } from "node:path";
3
11
  import { pathToFileURL } from "node:url";
4
-
5
- import { isPlainObject } from "es-toolkit";
6
- import { get, set } from "es-toolkit/compat";
7
- import { mergeAndConcat } from "merge-anything";
8
-
9
- /**
10
- * 加载配置选项
11
- * - 该类型为 scanConfig 专用:按“就近放置”原则直接与实现放在同文件,避免拆到单独的 *Types.ts 造成目录噪音。
12
- */
13
- export interface LoadConfigOptions {
14
- /** 当前工作目录,默认 process.cwd() */
15
- cwd?: string;
16
- /** 目录数组:要搜索的目录路径(相对于 cwd) */
17
- dirs: string[];
18
- /** 文件数组:要匹配的文件名 */
19
- files: string[];
20
- /** 文件扩展名,默认 ['.js', '.ts', '.json'] */
21
- extensions?: string[];
22
- /** 加载模式:'first' = 返回第一个找到的配置(默认),'merge' = 合并所有配置 */
23
- mode?: "merge" | "first";
24
- /** 指定要提取的字段路径数组,如 ['menus', 'database.host'],为空则返回完整对象 */
25
- paths?: string[];
26
- /** 默认配置,会与找到的配置合并(优先级最低) */
27
- defaults?: Record<string, any>;
28
- }
29
-
12
+ import { mergeAndConcat } from "./mergeAndConcat";
13
+ import { getByPath, isPlainObject, setByPath } from "./util";
30
14
  /**
31
15
  * 扫描并合并配置文件(矩阵搜索:dirs × files)
32
16
  * @param options - 加载选项
33
17
  * @returns 合并后的配置对象(或第一个找到的配置)
34
18
  */
35
- export async function scanConfig(options: LoadConfigOptions): Promise<Record<string, any>> {
36
- const {
37
- //
38
- cwd = process.cwd(),
39
- dirs,
40
- files,
41
- extensions = [".ts", ".ts", ".json"],
42
- mode = "first",
43
- paths,
44
- defaults = {}
45
- } = options;
46
-
19
+ export async function scanConfig(options) {
20
+ const {
21
+ //
22
+ cwd = process.cwd(), dirs, files, extensions = [".ts", ".js", ".json"], mode = "first", paths, defaults = {} } = options;
47
23
  // 参数验证
48
24
  if (!Array.isArray(dirs) || dirs.length === 0) {
49
25
  throw new Error("dirs 必须是非空数组");
@@ -51,92 +27,78 @@ export async function scanConfig(options: LoadConfigOptions): Promise<Record<str
51
27
  if (!Array.isArray(files) || files.length === 0) {
52
28
  throw new Error("files 必须是非空数组");
53
29
  }
54
-
55
- const configs: Record<string, any>[] = [];
56
-
30
+ const configs = [];
57
31
  // 矩阵搜索:dirs × files × extensions
58
32
  for (const dir of dirs) {
59
33
  // 如果是绝对路径则直接使用,否则拼接 cwd
60
34
  const fullDir = isAbsolute(dir) ? dir : join(cwd, dir);
61
-
62
35
  for (const file of files) {
63
36
  for (const ext of extensions) {
64
37
  const fileName = file.endsWith(ext) ? file : file + ext;
65
38
  const filePath = join(fullDir, fileName);
66
-
67
39
  if (existsSync(filePath)) {
68
40
  try {
69
41
  const importUrl = pathToFileURL(filePath).href + `?t=${Date.now()}`;
70
-
71
42
  // 动态导入配置文件(使用 import 断言处理 JSON)
72
- let data: any;
73
-
43
+ let data;
74
44
  if (ext === ".json") {
75
45
  // JSON 文件使用 import 断言
76
- const module = await import(importUrl, { with: { type: "json" } });
46
+ const module = await import(__rewriteRelativeImportExtension(importUrl), { with: { type: "json" } });
77
47
  data = module.default;
78
- } else {
48
+ }
49
+ else {
79
50
  // JS/TS 文件使用动态导入
80
- const module = await import(importUrl);
51
+ const module = await import(__rewriteRelativeImportExtension(importUrl));
81
52
  data = module.default || module;
82
-
83
53
  // 处理 async 函数导出(如 defineAddonConfig)
84
54
  if (data instanceof Promise) {
85
55
  data = await data;
86
56
  }
87
57
  }
88
-
89
58
  // 验证配置数据
90
59
  if (!isPlainObject(data)) {
91
60
  continue;
92
61
  }
93
-
94
62
  configs.push(data);
95
-
96
63
  // 如果模式为 'first',找到第一个配置后立即返回
97
64
  if (mode === "first") {
98
65
  const firstConfig = mergeAndConcat(defaults, data);
99
-
100
66
  // 如果指定了 paths,则只返回指定路径的字段
101
67
  if (paths && paths.length > 0) {
102
- const result: Record<string, any> = {};
68
+ const result = {};
103
69
  for (const path of paths) {
104
- const value = get(firstConfig, path);
70
+ const value = getByPath(firstConfig, path);
105
71
  if (value !== undefined) {
106
- set(result, path, value);
72
+ setByPath(result, path, value);
107
73
  }
108
74
  }
109
75
  return result;
110
76
  }
111
-
112
77
  return firstConfig;
113
78
  }
114
-
115
79
  // 找到后跳过同名文件的其他扩展名
116
80
  break;
117
- } catch {
81
+ }
82
+ catch {
118
83
  // 保持静默:继续尝试下一个候选配置文件
119
84
  }
120
85
  }
121
86
  }
122
87
  }
123
88
  }
124
-
125
89
  // 合并配置(使用 mergeAndConcat 深度合并)
126
90
  // 合并顺序:defaults ← configs[0] ← configs[1] ← ...
127
91
  const finalConfig = mergeAndConcat(defaults, ...configs);
128
-
129
92
  // 如果指定了 paths,则只返回指定路径的字段
130
93
  if (paths && paths.length > 0) {
131
- const result: Record<string, any> = {};
94
+ const result = {};
132
95
  for (const path of paths) {
133
- const value = get(finalConfig, path);
96
+ const value = getByPath(finalConfig, path);
134
97
  if (value !== undefined) {
135
- set(result, path, value);
98
+ setByPath(result, path, value);
136
99
  }
137
100
  }
138
101
  return result;
139
102
  }
140
-
141
103
  return finalConfig;
142
104
  }
@@ -0,0 +1,3 @@
1
+ import type { ScanFileResult } from "./scanFiles";
2
+ export declare function scanCoreBuiltinPlugins(): ScanFileResult[];
3
+ export declare function scanCoreBuiltinHooks(): ScanFileResult[];
@@ -0,0 +1,65 @@
1
+ import coreHookAuth from "../hooks/auth";
2
+ import coreHookCors from "../hooks/cors";
3
+ import coreHookParser from "../hooks/parser";
4
+ import coreHookPermission from "../hooks/permission";
5
+ import coreHookValidator from "../hooks/validator";
6
+ import corePluginCache from "../plugins/cache";
7
+ import corePluginCipher from "../plugins/cipher";
8
+ import corePluginConfig from "../plugins/config";
9
+ import corePluginDb from "../plugins/db";
10
+ import corePluginJwt from "../plugins/jwt";
11
+ import corePluginLogger from "../plugins/logger";
12
+ import corePluginRedis from "../plugins/redis";
13
+ import corePluginTool from "../plugins/tool";
14
+ function toCoreBuiltinScanFileResult(type, item) {
15
+ const name = item.name;
16
+ return {
17
+ source: "core",
18
+ type: type,
19
+ sourceName: "核心",
20
+ filePath: `core:${type}:${name}`,
21
+ relativePath: name,
22
+ fileName: name,
23
+ moduleName: name,
24
+ addonName: "",
25
+ fileBaseName: name,
26
+ fileDir: "(builtin)",
27
+ name: name,
28
+ enable: item ? item.enable : undefined,
29
+ deps: Array.isArray(item && item.deps) ? item.deps : [],
30
+ handler: item ? item.handler : null
31
+ };
32
+ }
33
+ export function scanCoreBuiltinPlugins() {
34
+ const plugins = [];
35
+ const builtinPlugins = [
36
+ //
37
+ corePluginLogger,
38
+ corePluginRedis,
39
+ corePluginDb,
40
+ corePluginCache,
41
+ corePluginTool,
42
+ corePluginCipher,
43
+ corePluginJwt,
44
+ corePluginConfig
45
+ ];
46
+ for (const plugin of builtinPlugins) {
47
+ plugins.push(toCoreBuiltinScanFileResult("plugin", plugin));
48
+ }
49
+ return plugins;
50
+ }
51
+ export function scanCoreBuiltinHooks() {
52
+ const hooks = [];
53
+ const builtinHooks = [
54
+ //
55
+ coreHookAuth,
56
+ coreHookCors,
57
+ coreHookParser,
58
+ coreHookValidator,
59
+ coreHookPermission
60
+ ];
61
+ for (const hook of builtinHooks) {
62
+ hooks.push(toCoreBuiltinScanFileResult("hook", hook));
63
+ }
64
+ return hooks;
65
+ }
@@ -0,0 +1,30 @@
1
+ export type ScanFileSource = "app" | "addon" | "core";
2
+ export type ScanFileType = "api" | "table" | "plugin" | "hook";
3
+ export interface ScanFileResultBase {
4
+ source: ScanFileSource;
5
+ type: ScanFileType;
6
+ sourceName: string;
7
+ filePath: string;
8
+ relativePath: string;
9
+ fileName: string;
10
+ /** 模块名(用于 deps 依赖图 key 与运行时挂载 key) */
11
+ moduleName: string;
12
+ /** addon 名:addon 来源为真实值;core/app 统一为空字符串("") */
13
+ addonName: string;
14
+ fileBaseName: string;
15
+ fileDir: string;
16
+ }
17
+ export type ScanFileResult = (ScanFileResultBase & {
18
+ type: "table";
19
+ content: any;
20
+ }) | (ScanFileResultBase & {
21
+ type: Exclude<ScanFileType, "table">;
22
+ } & Record<string, any>);
23
+ /**
24
+ * 扫描指定目录下的文件
25
+ * @param dir 目录路径
26
+ * @param source 文件来源(app/addon/core)
27
+ * @param type 文件类型(api/table/plugin/hook)
28
+ * @param pattern Glob模式
29
+ */
30
+ export declare function scanFiles(dir: string, source: ScanFileSource, type: ScanFileType, pattern: string): Promise<ScanFileResult[]>;
@@ -1,52 +1,30 @@
1
1
  import { existsSync } from "node:fs";
2
-
3
- import { forOwn } from "es-toolkit/compat";
4
- import { camelCase } from "es-toolkit/string";
5
2
  import { relative, normalize, parse, join } from "pathe";
6
-
7
- import { importDefault } from "./importDefault.ts";
8
-
9
- export type ScanFileSource = "app" | "addon" | "core";
10
-
11
- export type ScanFileType = "api" | "table" | "plugin" | "hook";
12
-
13
- export interface ScanFileResultBase {
14
- source: ScanFileSource; // 文件来源
15
- type: ScanFileType; // 文件类型(api/table/plugin/hook)
16
- sourceName: string; // 来源名称(用于日志展示)
17
- filePath: string; // 绝对路径
18
- relativePath: string; // 相对路径(无扩展名)
19
- fileName: string; // 文件名(无扩展名)
20
-
21
- /** 模块名(用于 deps 依赖图 key 与运行时挂载 key) */
22
- moduleName: string;
23
-
24
- /** addon 名:addon 来源为真实值;core/app 统一为空字符串("") */
25
- addonName: string;
26
-
27
- fileBaseName: string;
28
- fileDir: string;
29
- }
30
-
31
- export type ScanFileResult =
32
- | (ScanFileResultBase & {
33
- type: "table";
34
- content: any;
35
- })
36
- | (ScanFileResultBase & {
37
- type: Exclude<ScanFileType, "table">;
38
- } & Record<string, any>);
39
-
40
- function parseAddonNameFromPath(normalizedPath: string): string | null {
3
+ import { importDefault } from "./importDefault";
4
+ import { camelCase, forOwn } from "./util";
5
+ function parseAddonNameFromPath(normalizedPath) {
41
6
  // 期望路径中包含:/node_modules/@befly-addon/<addonName>/...
42
7
  const parts = normalizedPath.split("/").filter(Boolean);
43
8
  const idx = parts.indexOf("@befly-addon");
44
- if (idx < 0) return null;
9
+ if (idx < 0)
10
+ return null;
11
+ const addonName = parts[idx + 1];
12
+ if (typeof addonName !== "string" || addonName.trim() === "")
13
+ return null;
14
+ return addonName;
15
+ }
16
+ function parseLocalAddonNameFromPath(normalizedPath) {
17
+ // 兼容项目本地 addons:/addons/<addonName>/...
18
+ // 注意:normalizedPath 已通过 pathe.normalize 统一为 /
19
+ const parts = normalizedPath.split("/").filter(Boolean);
20
+ const idx = parts.lastIndexOf("addons");
21
+ if (idx < 0)
22
+ return null;
45
23
  const addonName = parts[idx + 1];
46
- if (typeof addonName !== "string" || addonName.trim() === "") return null;
24
+ if (typeof addonName !== "string" || addonName.trim() === "")
25
+ return null;
47
26
  return addonName;
48
27
  }
49
-
50
28
  /**
51
29
  * 扫描指定目录下的文件
52
30
  * @param dir 目录路径
@@ -54,13 +32,12 @@ function parseAddonNameFromPath(normalizedPath: string): string | null {
54
32
  * @param type 文件类型(api/table/plugin/hook)
55
33
  * @param pattern Glob模式
56
34
  */
57
- export async function scanFiles(dir: string, source: ScanFileSource, type: ScanFileType, pattern: string): Promise<ScanFileResult[]> {
58
- if (!existsSync(dir)) return [];
59
-
35
+ export async function scanFiles(dir, source, type, pattern) {
36
+ if (!existsSync(dir))
37
+ return [];
60
38
  const normalizedDir = normalize(dir);
61
39
  const glob = new Bun.Glob(pattern);
62
- const results: ScanFileResult[] = [];
63
-
40
+ const results = [];
64
41
  try {
65
42
  const files = await glob.scan({
66
43
  cwd: dir,
@@ -68,44 +45,43 @@ export async function scanFiles(dir: string, source: ScanFileSource, type: ScanF
68
45
  absolute: true,
69
46
  followSymlinks: true
70
47
  });
71
-
72
48
  for await (const file of files) {
73
- if (file.endsWith(".d.ts")) continue;
74
-
49
+ if (file.endsWith(".d.ts"))
50
+ continue;
75
51
  // 使用 pathe.normalize 统一路径分隔符为 /
76
52
  const normalizedFile = normalize(file);
77
-
78
53
  // 获取文件名(去除扩展名)
79
54
  const fileName = parse(normalizedFile).name;
80
-
81
55
  // 计算相对路径(去除扩展名)
82
56
  const relativePathWithExt = relative(normalizedDir, normalizedFile);
83
57
  const parsedRelativePath = parse(relativePathWithExt);
84
58
  const relativePath = parsedRelativePath.dir ? join(parsedRelativePath.dir, parsedRelativePath.name) : parsedRelativePath.name;
85
-
86
59
  // 固定默认过滤(不可关闭):忽略下划线开头的文件/目录
87
- if (fileName.startsWith("_")) continue;
88
- if (relativePath.split("/").some((part) => part.startsWith("_"))) continue;
60
+ if (fileName.startsWith("_"))
61
+ continue;
62
+ if (relativePath.split("/").some((part) => part.startsWith("_")))
63
+ continue;
89
64
  const content = await importDefault(normalizedFile, {});
90
-
91
65
  const baseName = camelCase(fileName);
92
66
  let addonName = "";
93
67
  let moduleName = "";
94
-
95
68
  if (source === "core") {
96
- moduleName = baseName;
97
- } else if (source === "app") {
69
+ // core:不做任何命名转换,直接使用文件名作为 moduleName(例如 auth / rate_limit)。
70
+ addonName = "";
71
+ moduleName = fileName;
72
+ }
73
+ else if (source === "app") {
98
74
  moduleName = `app_${baseName}`;
99
- } else {
100
- const parsedAddonName = parseAddonNameFromPath(normalizedFile);
75
+ }
76
+ else {
77
+ const parsedAddonName = parseAddonNameFromPath(normalizedFile) || parseLocalAddonNameFromPath(normalizedFile);
101
78
  if (!parsedAddonName) {
102
79
  throw new Error(`scanFiles addon moduleName 解析失败:未找到 @befly-addon/<addon>/ 段落:${normalizedFile}`);
103
80
  }
104
81
  addonName = parsedAddonName;
105
82
  moduleName = `addon_${camelCase(addonName)}_${baseName}`;
106
83
  }
107
-
108
- const base: Record<string, any> = {
84
+ const base = {
109
85
  source: source,
110
86
  type: type,
111
87
  sourceName: { core: "核心", addon: "组件", app: "项目" }[source],
@@ -117,10 +93,9 @@ export async function scanFiles(dir: string, source: ScanFileSource, type: ScanF
117
93
  fileBaseName: parse(normalizedFile).base,
118
94
  fileDir: dir
119
95
  };
120
-
121
96
  if (type === "table") {
122
97
  base.content = content;
123
- results.push(base as ScanFileResult);
98
+ results.push(base);
124
99
  continue;
125
100
  }
126
101
  if (type === "api") {
@@ -135,21 +110,19 @@ export async function scanFiles(dir: string, source: ScanFileSource, type: ScanF
135
110
  base.name = "";
136
111
  base.handler = null;
137
112
  }
138
-
139
113
  forOwn(content, (value, key) => {
140
114
  base[key] = value;
141
115
  });
142
-
143
116
  if (type === "api") {
144
- base.routePrefix = source === "core" ? "/core" : source === "app" ? "/app" : `/addon/${addonName}`;
117
+ base.routePrefix = source === "app" ? "/app" : `/addon/${addonName}`;
145
118
  base.routePath = `/api${base.routePrefix}/${relativePath}`;
146
119
  }
147
-
148
- results.push(base as ScanFileResult);
120
+ results.push(base);
149
121
  }
150
- } catch (error: any) {
122
+ }
123
+ catch (error) {
151
124
  const wrappedError = new Error(`scanFiles failed: source=${source} type=${type} dir=${normalizedDir} pattern=${pattern}`);
152
- (wrappedError as any).cause = error;
125
+ wrappedError.cause = error;
153
126
  throw wrappedError;
154
127
  }
155
128
  return results;
@@ -0,0 +1,10 @@
1
+ import type { AddonInfo } from "./scanAddons";
2
+ import type { ScanFileResult } from "./scanFiles";
3
+ export type ScanSourcesResult = {
4
+ hooks: ScanFileResult[];
5
+ plugins: ScanFileResult[];
6
+ apis: ScanFileResult[];
7
+ tables: ScanFileResult[];
8
+ addons: AddonInfo[];
9
+ };
10
+ export declare const scanSources: () => Promise<ScanSourcesResult>;
@@ -0,0 +1,46 @@
1
+ import { join } from "pathe";
2
+ import { appDir } from "../paths";
3
+ import { scanAddons } from "./scanAddons";
4
+ import { scanCoreBuiltinHooks, scanCoreBuiltinPlugins } from "./scanCoreBuiltins";
5
+ import { scanFiles } from "./scanFiles";
6
+ export const scanSources = async () => {
7
+ const apis = [];
8
+ const plugins = [];
9
+ const hooks = [];
10
+ const tables = [];
11
+ const addons = await scanAddons();
12
+ // 处理表格
13
+ tables.push(...(await scanFiles(join(appDir, "tables"), "app", "table", "*.json")));
14
+ for (const addon of addons) {
15
+ tables.push(...(await scanFiles(join(addon.fullPath, "tables"), "addon", "table", "*.json")));
16
+ }
17
+ // 处理插件
18
+ // core 内置插件:必须静态导入(支持 bun bundle 单文件)。
19
+ // 约束:core 插件名由 export default.name 指定。
20
+ plugins.push(...scanCoreBuiltinPlugins());
21
+ plugins.push(...(await scanFiles(join(appDir, "plugins"), "app", "plugin", "*.{ts,js}")));
22
+ for (const addon of addons) {
23
+ plugins.push(...(await scanFiles(join(addon.fullPath, "plugins"), "addon", "plugin", "*.{ts,js}")));
24
+ }
25
+ // 处理钩子
26
+ // core 内置钩子:必须静态导入(支持 bun bundle 单文件)。
27
+ // 约束:core 钩子名由 export default.name 指定。
28
+ hooks.push(...scanCoreBuiltinHooks());
29
+ hooks.push(...(await scanFiles(join(appDir, "hooks"), "app", "hook", "*.{ts,js}")));
30
+ for (const addon of addons) {
31
+ hooks.push(...(await scanFiles(join(addon.fullPath, "hooks"), "addon", "hook", "*.{ts,js}")));
32
+ }
33
+ // 处理接口
34
+ // 说明:core 没有内置 apis;接口只从「项目(app)」与「组件(addon)」中扫描加载。
35
+ apis.push(...(await scanFiles(join(appDir, "apis"), "app", "api", "**/*.{ts,js}")));
36
+ for (const addon of addons) {
37
+ apis.push(...(await scanFiles(join(addon.fullPath, "apis"), "addon", "api", "**/*.{ts,js}")));
38
+ }
39
+ return {
40
+ hooks: hooks,
41
+ plugins: plugins,
42
+ apis: apis,
43
+ tables: tables,
44
+ addons: addons
45
+ };
46
+ };
@@ -0,0 +1,28 @@
1
+ export type SortModulesByDepsOptions<T> = {
2
+ /**
3
+ * 用于日志的模块标签(如:"插件"、"钩子")
4
+ */
5
+ moduleLabel?: string;
6
+ /**
7
+ * 生成模块名(用于 deps 解析与排序 key)。
8
+ * 默认:camelCase(item.fileName)
9
+ */
10
+ getName?: (item: T) => string;
11
+ /**
12
+ * 获取 deps。
13
+ * 默认:item.deps
14
+ */
15
+ getDeps?: (item: T) => string[];
16
+ };
17
+ /**
18
+ * 按 deps 拓扑排序 scanSources 扫描得到的插件/钩子。
19
+ *
20
+ * 说明:
21
+ * - 输入为 scanSources/scanFiles 的条目数组:每个条目包含 fileName 与 deps。
22
+ * - deps 里的字符串会与 getName(item) 的结果匹配。
23
+ * - 若出现:重复 name、缺失依赖、循环依赖,则返回 false。
24
+ */
25
+ export declare function sortModules<T extends {
26
+ fileName: string;
27
+ deps?: any;
28
+ }>(items: T[], options?: SortModulesByDepsOptions<T>): T[] | false;