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.
- package/README.md +83 -307
- package/dist/befly.config.d.ts +7 -0
- package/{befly.config.ts → dist/befly.config.js} +11 -36
- package/dist/befly.js +15621 -0
- package/dist/befly.min.js +21 -0
- package/dist/checks/checkApi.d.ts +1 -0
- package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
- package/dist/checks/checkHook.d.ts +1 -0
- package/dist/checks/checkHook.js +86 -0
- package/dist/checks/checkMenu.d.ts +7 -0
- package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
- package/dist/checks/checkPlugin.d.ts +1 -0
- package/dist/checks/checkPlugin.js +86 -0
- package/dist/checks/checkTable.d.ts +6 -0
- package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
- package/dist/configs/presetFields.d.ts +4 -0
- package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
- package/dist/configs/presetRegexp.d.ts +145 -0
- package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
- package/dist/hooks/auth.d.ts +7 -0
- package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
- package/dist/hooks/cors.d.ts +11 -0
- package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
- package/dist/hooks/parser.d.ts +14 -0
- package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
- package/dist/hooks/permission.d.ts +14 -0
- package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
- package/dist/hooks/validator.d.ts +11 -0
- package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
- package/dist/index.d.ts +26 -0
- package/{main.ts → dist/index.js} +61 -100
- package/dist/lib/asyncContext.d.ts +21 -0
- package/dist/lib/asyncContext.js +27 -0
- package/dist/lib/cacheHelper.d.ts +95 -0
- package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
- package/dist/lib/cacheKeys.d.ts +23 -0
- package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
- package/dist/lib/cipher.d.ts +153 -0
- package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
- package/dist/lib/connect.d.ts +91 -0
- package/{lib/connect.ts → dist/lib/connect.js} +47 -88
- package/dist/lib/dbDialect.d.ts +87 -0
- package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
- package/dist/lib/dbHelper.d.ts +204 -0
- package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
- package/dist/lib/dbUtils.d.ts +68 -0
- package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
- package/dist/lib/jwt.d.ts +13 -0
- package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
- package/dist/lib/logger.d.ts +42 -0
- package/dist/lib/logger.js +1144 -0
- package/dist/lib/redisHelper.d.ts +185 -0
- package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
- package/dist/lib/sqlBuilder.d.ts +160 -0
- package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
- package/dist/lib/sqlCheck.d.ts +23 -0
- package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
- package/dist/lib/validator.d.ts +45 -0
- package/{lib/validator.ts → dist/lib/validator.js} +44 -61
- package/dist/loader/loadApis.d.ts +12 -0
- package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
- package/dist/loader/loadHooks.d.ts +7 -0
- package/dist/loader/loadHooks.js +35 -0
- package/dist/loader/loadPlugins.d.ts +8 -0
- package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
- package/dist/paths.d.ts +93 -0
- package/{paths.ts → dist/paths.js} +6 -19
- package/dist/plugins/cache.d.ts +16 -0
- package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
- package/dist/plugins/cipher.d.ts +12 -0
- package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
- package/dist/plugins/config.d.ts +12 -0
- package/dist/plugins/config.js +8 -0
- package/dist/plugins/db.d.ts +16 -0
- package/{plugins/db.ts → dist/plugins/db.js} +11 -17
- package/dist/plugins/jwt.d.ts +12 -0
- package/dist/plugins/jwt.js +12 -0
- package/dist/plugins/logger.d.ts +32 -0
- package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
- package/dist/plugins/redis.d.ts +16 -0
- package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
- package/dist/plugins/tool.d.ts +81 -0
- package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
- package/dist/router/api.d.ts +14 -0
- package/dist/router/api.js +107 -0
- package/dist/router/static.d.ts +9 -0
- package/{router/static.ts → dist/router/static.js} +20 -34
- package/dist/scripts/ensureDist.d.ts +1 -0
- package/dist/scripts/ensureDist.js +296 -0
- package/dist/sync/syncApi.d.ts +3 -0
- package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
- package/dist/sync/syncCache.d.ts +2 -0
- package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
- package/dist/sync/syncDev.d.ts +6 -0
- package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
- package/dist/sync/syncMenu.d.ts +14 -0
- package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
- package/dist/sync/syncTable.d.ts +151 -0
- package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
- package/{types → dist/types}/api.d.ts +12 -51
- package/dist/types/api.js +4 -0
- package/{types → dist/types}/befly.d.ts +32 -227
- package/dist/types/befly.js +4 -0
- package/{types → dist/types}/cache.d.ts +7 -15
- package/dist/types/cache.js +4 -0
- package/dist/types/cipher.d.ts +27 -0
- package/dist/types/cipher.js +7 -0
- package/{types → dist/types}/common.d.ts +8 -33
- package/dist/types/common.js +5 -0
- package/{types → dist/types}/context.d.ts +3 -5
- package/dist/types/context.js +4 -0
- package/{types → dist/types}/crypto.d.ts +0 -3
- package/dist/types/crypto.js +4 -0
- package/dist/types/database.d.ts +138 -0
- package/dist/types/database.js +4 -0
- package/dist/types/hook.d.ts +17 -0
- package/dist/types/hook.js +6 -0
- package/dist/types/jwt.d.ts +75 -0
- package/dist/types/jwt.js +4 -0
- package/dist/types/logger.d.ts +59 -0
- package/dist/types/logger.js +6 -0
- package/dist/types/plugin.d.ts +16 -0
- package/dist/types/plugin.js +6 -0
- package/dist/types/redis.d.ts +71 -0
- package/dist/types/redis.js +4 -0
- package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
- package/dist/types/roleApisCache.js +8 -0
- package/dist/types/sync.d.ts +92 -0
- package/dist/types/sync.js +4 -0
- package/dist/types/table.d.ts +34 -0
- package/dist/types/table.js +4 -0
- package/dist/types/validate.d.ts +67 -0
- package/dist/types/validate.js +4 -0
- package/dist/utils/calcPerfTime.d.ts +4 -0
- package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
- package/dist/utils/convertBigIntFields.d.ts +11 -0
- package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
- package/dist/utils/cors.d.ts +8 -0
- package/{utils/cors.ts → dist/utils/cors.js} +1 -3
- package/dist/utils/disableMenusGlob.d.ts +13 -0
- package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
- package/dist/utils/fieldClear.d.ts +11 -0
- package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
- package/dist/utils/getClientIp.d.ts +6 -0
- package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
- package/dist/utils/importDefault.d.ts +1 -0
- package/dist/utils/importDefault.js +29 -0
- package/dist/utils/isDirentDirectory.d.ts +2 -0
- package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
- package/dist/utils/loadMenuConfigs.d.ts +29 -0
- package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
- package/dist/utils/mergeAndConcat.d.ts +7 -0
- package/dist/utils/mergeAndConcat.js +72 -0
- package/dist/utils/processAtSymbol.d.ts +4 -0
- package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
- package/dist/utils/processInfo.d.ts +24 -0
- package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
- package/dist/utils/response.d.ts +20 -0
- package/{utils/response.ts → dist/utils/response.js} +28 -49
- package/dist/utils/scanAddons.d.ts +17 -0
- package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
- package/dist/utils/scanConfig.d.ts +26 -0
- package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
- package/dist/utils/scanCoreBuiltins.d.ts +3 -0
- package/dist/utils/scanCoreBuiltins.js +65 -0
- package/dist/utils/scanFiles.d.ts +30 -0
- package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
- package/dist/utils/scanSources.d.ts +10 -0
- package/dist/utils/scanSources.js +46 -0
- package/dist/utils/sortModules.d.ts +28 -0
- package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
- package/dist/utils/util.d.ts +84 -0
- package/dist/utils/util.js +262 -0
- package/package.json +26 -34
- package/.gitignore +0 -0
- package/bunfig.toml +0 -3
- package/checks/checkHook.ts +0 -48
- package/checks/checkPlugin.ts +0 -48
- package/configs/presetRegexp.ts +0 -225
- package/docs/README.md +0 -98
- package/docs/api/api.md +0 -1921
- package/docs/guide/examples.md +0 -926
- package/docs/guide/quickstart.md +0 -354
- package/docs/hooks/auth.md +0 -38
- package/docs/hooks/cors.md +0 -28
- package/docs/hooks/hook.md +0 -838
- package/docs/hooks/parser.md +0 -19
- package/docs/hooks/rateLimit.md +0 -47
- package/docs/infra/redis.md +0 -628
- package/docs/plugins/cipher.md +0 -61
- package/docs/plugins/database.md +0 -189
- package/docs/plugins/plugin.md +0 -986
- package/docs/reference/addon.md +0 -510
- package/docs/reference/config.md +0 -573
- package/docs/reference/logger.md +0 -495
- package/docs/reference/sync.md +0 -478
- package/docs/reference/table.md +0 -763
- package/docs/reference/validator.md +0 -620
- package/lib/asyncContext.ts +0 -43
- package/lib/logger.ts +0 -811
- package/loader/loadHooks.ts +0 -51
- package/plugins/config.ts +0 -13
- package/plugins/jwt.ts +0 -15
- package/router/api.ts +0 -130
- package/tsconfig.json +0 -8
- package/types/database.d.ts +0 -541
- package/types/hook.d.ts +0 -25
- package/types/jwt.d.ts +0 -118
- package/types/logger.d.ts +0 -65
- package/types/plugin.d.ts +0 -19
- package/types/redis.d.ts +0 -83
- package/types/sync.d.ts +0 -398
- package/types/table.d.ts +0 -216
- package/types/validate.d.ts +0 -69
- package/utils/arrayKeysToCamel.ts +0 -18
- package/utils/configTypes.ts +0 -3
- package/utils/genShortId.ts +0 -12
- package/utils/importDefault.ts +0 -21
- package/utils/keysToCamel.ts +0 -22
- package/utils/keysToSnake.ts +0 -22
- package/utils/pickFields.ts +0 -19
- package/utils/scanSources.ts +0 -64
- package/utils/sqlLog.ts +0 -37
|
@@ -1,26 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
|
11
|
+
export function sortModules(items, options = {}) {
|
|
33
12
|
const moduleLabel = options.moduleLabel || "模块";
|
|
34
|
-
const getName =
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
69
|
+
if (!isPass)
|
|
70
|
+
return false;
|
|
107
71
|
// 3) 拓扑排序(DFS)
|
|
108
|
-
const visit = (name
|
|
109
|
-
if (visited.has(name))
|
|
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)
|
|
118
|
-
|
|
119
|
-
const deps = getDeps(item)
|
|
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.
|
|
4
|
-
"gitHead": "
|
|
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
|
-
"
|
|
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": "
|
|
46
|
-
"types": "
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
47
30
|
"exports": {
|
|
48
31
|
".": {
|
|
49
|
-
"types": "./
|
|
50
|
-
"default": "./
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
51
34
|
},
|
|
52
|
-
"./
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
"
|
|
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
package/checks/checkHook.ts
DELETED
|
@@ -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
|
-
}
|