befly 3.10.17 → 3.10.19
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} +8 -31
- package/dist/checks/checkApi.d.ts +1 -0
- package/{checks/checkApi.ts → dist/checks/checkApi.js} +10 -27
- package/dist/checks/checkHook.d.ts +1 -0
- package/{checks/checkHook.ts → dist/checks/checkHook.js} +10 -19
- package/dist/checks/checkMenu.d.ts +7 -0
- package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +15 -50
- package/dist/checks/checkPlugin.d.ts +1 -0
- package/{checks/checkPlugin.ts → dist/checks/checkPlugin.js} +10 -19
- package/dist/checks/checkTable.d.ts +6 -0
- package/{checks/checkTable.ts → dist/checks/checkTable.js} +16 -40
- 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 +5 -0
- package/{hooks/auth.ts → dist/hooks/auth.js} +5 -9
- package/dist/hooks/cors.d.ts +9 -0
- package/{hooks/cors.ts → dist/hooks/cors.js} +2 -12
- package/dist/hooks/parser.d.ts +12 -0
- package/{hooks/parser.ts → dist/hooks/parser.js} +27 -42
- package/dist/hooks/permission.d.ts +12 -0
- package/{hooks/permission.ts → dist/hooks/permission.js} +11 -22
- package/dist/hooks/validator.d.ts +9 -0
- package/{hooks/validator.ts → dist/hooks/validator.js} +5 -12
- 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} +43 -103
- 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} +46 -87
- 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} +73 -230
- package/dist/lib/dbUtils.d.ts +68 -0
- package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +49 -123
- package/dist/lib/jwt.d.ts +13 -0
- package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
- package/dist/lib/logger.d.ts +32 -0
- package/{lib/logger.ts → dist/lib/logger.js} +201 -278
- package/dist/lib/redisHelper.d.ts +185 -0
- package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +95 -139
- package/dist/lib/sqlBuilder.d.ts +160 -0
- package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +131 -277
- 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} +43 -60
- package/dist/loader/loadApis.d.ts +12 -0
- package/{loader/loadApis.ts → dist/loader/loadApis.js} +7 -17
- package/dist/loader/loadHooks.d.ts +8 -0
- package/{loader/loadHooks.ts → dist/loader/loadHooks.js} +5 -19
- package/dist/loader/loadPlugins.d.ts +8 -0
- package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +8 -20
- package/dist/main.d.ts +26 -0
- package/{main.ts → dist/main.js} +39 -78
- package/dist/paths.d.ts +93 -0
- package/{paths.ts → dist/paths.js} +6 -19
- package/dist/plugins/cache.d.ts +14 -0
- package/{plugins/cache.ts → dist/plugins/cache.js} +4 -11
- package/dist/plugins/cipher.d.ts +10 -0
- package/{plugins/cipher.ts → dist/plugins/cipher.js} +1 -5
- package/dist/plugins/config.d.ts +10 -0
- package/dist/plugins/config.js +6 -0
- package/dist/plugins/db.d.ts +14 -0
- package/{plugins/db.ts → dist/plugins/db.js} +5 -13
- package/dist/plugins/jwt.d.ts +10 -0
- package/{plugins/jwt.ts → dist/plugins/jwt.js} +2 -7
- package/dist/plugins/logger.d.ts +28 -0
- package/{plugins/logger.ts → dist/plugins/logger.js} +2 -7
- package/dist/plugins/redis.d.ts +14 -0
- package/{plugins/redis.ts → dist/plugins/redis.js} +4 -9
- package/dist/plugins/tool.d.ts +79 -0
- package/{plugins/tool.ts → dist/plugins/tool.js} +7 -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} +17 -31
- package/dist/scripts/ensureDist.d.ts +1 -0
- package/dist/scripts/ensureDist.js +80 -0
- package/dist/sync/syncApi.d.ts +3 -0
- package/{sync/syncApi.ts → dist/sync/syncApi.js} +33 -53
- 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} +27 -60
- package/dist/sync/syncMenu.d.ts +14 -0
- package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +61 -121
- package/dist/sync/syncTable.d.ts +151 -0
- package/{sync/syncTable.ts → dist/sync/syncTable.js} +168 -375
- package/{types → dist/types}/api.d.ts +12 -51
- package/dist/types/api.js +4 -0
- package/{types → dist/types}/befly.d.ts +32 -223
- 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 +15 -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 +47 -0
- package/dist/types/logger.js +6 -0
- package/dist/types/plugin.d.ts +14 -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/arrayKeysToCamel.d.ts +13 -0
- package/{utils/arrayKeysToCamel.ts → dist/utils/arrayKeysToCamel.js} +4 -4
- package/dist/utils/calcPerfTime.d.ts +4 -0
- package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
- package/dist/utils/configTypes.d.ts +1 -0
- package/dist/utils/configTypes.js +1 -0
- 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/genShortId.d.ts +10 -0
- package/{utils/genShortId.ts → dist/utils/genShortId.js} +1 -1
- 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/keysToCamel.d.ts +10 -0
- package/{utils/keysToCamel.ts → dist/utils/keysToCamel.js} +4 -5
- package/dist/utils/keysToSnake.d.ts +10 -0
- package/{utils/keysToSnake.ts → dist/utils/keysToSnake.js} +4 -5
- package/dist/utils/loadMenuConfigs.d.ts +5 -0
- package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +22 -49
- package/dist/utils/pickFields.d.ts +4 -0
- package/{utils/pickFields.ts → dist/utils/pickFields.js} +2 -5
- package/dist/utils/process.d.ts +24 -0
- package/{utils/process.ts → dist/utils/process.js} +2 -18
- package/dist/utils/processFields.d.ts +4 -0
- package/{utils/processFields.ts → dist/utils/processFields.js} +4 -8
- package/dist/utils/regex.d.ts +145 -0
- package/{configs/presetRegexp.ts → dist/utils/regex.js} +8 -31
- package/dist/utils/response.d.ts +20 -0
- package/{utils/response.ts → dist/utils/response.js} +27 -48
- package/dist/utils/scanAddons.d.ts +17 -0
- package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +4 -38
- package/dist/utils/scanConfig.d.ts +26 -0
- package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +22 -59
- package/dist/utils/scanFiles.d.ts +30 -0
- package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +25 -65
- package/dist/utils/scanSources.d.ts +10 -0
- package/{utils/scanSources.ts → dist/utils/scanSources.js} +16 -39
- package/dist/utils/sortModules.d.ts +28 -0
- package/{utils/sortModules.ts → dist/utils/sortModules.js} +24 -64
- package/dist/utils/sqlLog.d.ts +14 -0
- package/{utils/sqlLog.ts → dist/utils/sqlLog.js} +2 -14
- package/package.json +15 -32
- package/.gitignore +0 -0
- package/bunfig.toml +0 -3
- 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/plugins/config.ts +0 -13
- package/router/api.ts +0 -130
- package/tsconfig.json +0 -54
- 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/configTypes.ts +0 -3
- package/utils/importDefault.ts +0 -21
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
2
|
import { omit } from "es-toolkit/object";
|
|
3
|
-
|
|
4
3
|
import { Logger } from "../lib/logger.js";
|
|
5
|
-
|
|
6
|
-
export async function checkHook(hooks: any[]): Promise<void> {
|
|
4
|
+
export async function checkHook(hooks) {
|
|
7
5
|
let hasError = false;
|
|
8
|
-
|
|
9
6
|
for (const hook of hooks) {
|
|
10
7
|
try {
|
|
11
8
|
if (!isPlainObject(hook)) {
|
|
@@ -13,35 +10,29 @@ export async function checkHook(hooks: any[]): Promise<void> {
|
|
|
13
10
|
hasError = true;
|
|
14
11
|
continue;
|
|
15
12
|
}
|
|
16
|
-
|
|
17
|
-
if (!Array.isArray((hook as any).deps)) {
|
|
13
|
+
if (!Array.isArray(hook.deps)) {
|
|
18
14
|
Logger.warn(omit(hook, ["handler"]), "钩子的 deps 属性必须是字符串数组");
|
|
19
15
|
hasError = true;
|
|
20
16
|
continue;
|
|
21
17
|
}
|
|
22
|
-
|
|
23
|
-
if ((hook as any).deps.some((depItem: any) => typeof depItem !== "string")) {
|
|
18
|
+
if (hook.deps.some((depItem) => typeof depItem !== "string")) {
|
|
24
19
|
Logger.warn(omit(hook, ["handler"]), "钩子的 deps 属性必须是字符串数组");
|
|
25
20
|
hasError = true;
|
|
26
21
|
}
|
|
27
|
-
|
|
28
|
-
if (typeof (hook as any).handler !== "function") {
|
|
22
|
+
if (typeof hook.handler !== "function") {
|
|
29
23
|
Logger.warn(omit(hook, ["handler"]), "钩子的 handler 属性必须是函数");
|
|
30
24
|
hasError = true;
|
|
31
25
|
continue;
|
|
32
26
|
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"钩子解析失败"
|
|
40
|
-
);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
Logger.error({
|
|
30
|
+
err: error,
|
|
31
|
+
item: hook
|
|
32
|
+
}, "钩子解析失败");
|
|
41
33
|
hasError = true;
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
|
-
|
|
45
36
|
if (hasError) {
|
|
46
37
|
throw new Error("钩子结构检查失败");
|
|
47
38
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MenuConfig } from "../types/sync.ts";
|
|
2
|
+
import type { AddonInfo } from "../utils/scanAddons.ts";
|
|
3
|
+
type CheckMenuOptions = {
|
|
4
|
+
disableMenus?: string[];
|
|
5
|
+
};
|
|
6
|
+
export declare const checkMenu: (addons: AddonInfo[], options?: CheckMenuOptions) => Promise<MenuConfig[]>;
|
|
7
|
+
export {};
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import type { MenuConfig } from "../types/sync.js";
|
|
2
|
-
import type { AddonInfo } from "../utils/scanAddons.js";
|
|
3
|
-
|
|
4
1
|
import { Logger } from "../lib/logger.js";
|
|
5
2
|
import { compileDisableMenuGlobRules, isMenuPathDisabledByGlobRules } from "../utils/disableMenusGlob.js";
|
|
6
3
|
import { loadMenuConfigs } from "../utils/loadMenuConfigs.js";
|
|
7
|
-
|
|
8
|
-
function isValidMenuPath(path: string): { ok: boolean; reason: string } {
|
|
4
|
+
function isValidMenuPath(path) {
|
|
9
5
|
if (!path) {
|
|
10
6
|
return { ok: false, reason: "path 不能为空" };
|
|
11
7
|
}
|
|
@@ -23,96 +19,74 @@ function isValidMenuPath(path: string): { ok: boolean; reason: string } {
|
|
|
23
19
|
}
|
|
24
20
|
return { ok: true, reason: "" };
|
|
25
21
|
}
|
|
26
|
-
|
|
27
|
-
type CheckMenuOptions = {
|
|
28
|
-
disableMenus?: string[];
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type DisableMenuRule = ReturnType<typeof compileDisableMenuGlobRules>[number];
|
|
32
|
-
|
|
33
|
-
function filterMenusByDisableRules(mergedMenus: MenuConfig[], rules: DisableMenuRule[]): MenuConfig[] {
|
|
22
|
+
function filterMenusByDisableRules(mergedMenus, rules) {
|
|
34
23
|
if (rules.length === 0) {
|
|
35
24
|
return mergedMenus;
|
|
36
25
|
}
|
|
37
|
-
|
|
38
|
-
const filtered: MenuConfig[] = [];
|
|
39
|
-
|
|
26
|
+
const filtered = [];
|
|
40
27
|
for (const menu of mergedMenus) {
|
|
41
|
-
const menuPath = typeof
|
|
28
|
+
const menuPath = typeof menu?.path === "string" ? String(menu.path).trim() : "";
|
|
42
29
|
if (menuPath && isMenuPathDisabledByGlobRules(menuPath, rules)) {
|
|
43
30
|
continue;
|
|
44
31
|
}
|
|
45
|
-
|
|
46
|
-
const children = Array.isArray((menu as any)?.children) ? ((menu as any).children as MenuConfig[]) : null;
|
|
32
|
+
const children = Array.isArray(menu?.children) ? menu.children : null;
|
|
47
33
|
if (children && children.length > 0) {
|
|
48
34
|
const nextChildren = filterMenusByDisableRules(children, rules);
|
|
49
35
|
if (nextChildren.length > 0) {
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
36
|
+
menu.children = nextChildren;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
delete menu.children;
|
|
53
40
|
}
|
|
54
41
|
}
|
|
55
|
-
|
|
56
42
|
filtered.push(menu);
|
|
57
43
|
}
|
|
58
|
-
|
|
59
44
|
return filtered;
|
|
60
45
|
}
|
|
61
|
-
|
|
62
|
-
export const checkMenu = async (addons: AddonInfo[], options: CheckMenuOptions = {}): Promise<MenuConfig[]> => {
|
|
46
|
+
export const checkMenu = async (addons, options = {}) => {
|
|
63
47
|
let hasError = false;
|
|
64
|
-
|
|
65
48
|
const mergedMenus = await loadMenuConfigs(addons);
|
|
66
|
-
|
|
67
49
|
const disableRules = compileDisableMenuGlobRules(options.disableMenus);
|
|
68
50
|
const filteredMenus = filterMenusByDisableRules(mergedMenus, disableRules);
|
|
69
|
-
|
|
70
|
-
const stack: Array<{ menu: any; depth: number }> = [];
|
|
51
|
+
const stack = [];
|
|
71
52
|
for (const m of filteredMenus) {
|
|
72
53
|
stack.push({ menu: m, depth: 1 });
|
|
73
54
|
}
|
|
74
|
-
|
|
75
|
-
const pathSet = new Set<string>();
|
|
76
|
-
|
|
55
|
+
const pathSet = new Set();
|
|
77
56
|
while (stack.length > 0) {
|
|
78
|
-
const current = stack.pop()
|
|
57
|
+
const current = stack.pop();
|
|
79
58
|
const menu = current?.menu;
|
|
80
59
|
const depth = typeof current?.depth === "number" ? current.depth : 0;
|
|
81
|
-
|
|
82
60
|
if (menu === null || typeof menu !== "object") {
|
|
83
61
|
hasError = true;
|
|
84
62
|
Logger.warn({ menu: menu }, "菜单节点必须是对象");
|
|
85
63
|
continue;
|
|
86
64
|
}
|
|
87
|
-
|
|
88
65
|
if (depth > 3) {
|
|
89
66
|
hasError = true;
|
|
90
67
|
Logger.warn({ path: menu?.path, depth: depth }, "菜单层级超过 3 级(最多三级)");
|
|
91
68
|
continue;
|
|
92
69
|
}
|
|
93
|
-
|
|
94
70
|
const children = menu.children;
|
|
95
71
|
if (typeof children !== "undefined" && !Array.isArray(children)) {
|
|
96
72
|
hasError = true;
|
|
97
73
|
Logger.warn({ path: menu?.path, childrenType: typeof children }, "菜单 children 必须是数组");
|
|
98
74
|
continue;
|
|
99
75
|
}
|
|
100
|
-
|
|
101
76
|
if (Array.isArray(children) && children.length > 0) {
|
|
102
77
|
if (depth >= 3) {
|
|
103
78
|
hasError = true;
|
|
104
79
|
Logger.warn({ path: menu?.path, depth: depth }, "菜单层级超过 3 级(最多三级)");
|
|
105
|
-
}
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
106
82
|
for (const child of children) {
|
|
107
83
|
stack.push({ menu: child, depth: depth + 1 });
|
|
108
84
|
}
|
|
109
85
|
}
|
|
110
86
|
}
|
|
111
|
-
|
|
112
87
|
const path = typeof menu.path === "string" ? menu.path.trim() : "";
|
|
113
88
|
const name = typeof menu.name === "string" ? menu.name.trim() : "";
|
|
114
89
|
const sort = typeof menu.sort === "number" ? menu.sort : 999999;
|
|
115
|
-
|
|
116
90
|
// 标准化输出(用于后续 syncMenu 直接使用)
|
|
117
91
|
if (typeof menu.path === "string") {
|
|
118
92
|
menu.path = path;
|
|
@@ -123,46 +97,37 @@ export const checkMenu = async (addons: AddonInfo[], options: CheckMenuOptions =
|
|
|
123
97
|
if (typeof menu.sort === "undefined") {
|
|
124
98
|
menu.sort = sort;
|
|
125
99
|
}
|
|
126
|
-
|
|
127
100
|
if (!path) {
|
|
128
101
|
hasError = true;
|
|
129
102
|
Logger.warn({ menu: menu }, "菜单缺少 path(必须是非空字符串)");
|
|
130
103
|
continue;
|
|
131
104
|
}
|
|
132
|
-
|
|
133
105
|
const pathCheck = isValidMenuPath(path);
|
|
134
106
|
if (!pathCheck.ok) {
|
|
135
107
|
hasError = true;
|
|
136
108
|
Logger.warn({ path: path, reason: pathCheck.reason }, "菜单 path 不合法");
|
|
137
109
|
}
|
|
138
|
-
|
|
139
110
|
if (!name) {
|
|
140
111
|
hasError = true;
|
|
141
112
|
Logger.warn({ path: path, menu: menu }, "菜单缺少 name(必须是非空字符串)");
|
|
142
113
|
}
|
|
143
|
-
|
|
144
114
|
if (typeof menu.sort !== "undefined" && typeof menu.sort !== "number") {
|
|
145
115
|
hasError = true;
|
|
146
116
|
Logger.warn({ path: path, sort: menu.sort }, "菜单 sort 必须是 number");
|
|
147
117
|
}
|
|
148
|
-
|
|
149
118
|
if (typeof menu.sort === "number" && (!Number.isFinite(menu.sort) || menu.sort < 1)) {
|
|
150
119
|
hasError = true;
|
|
151
120
|
Logger.warn({ path: path, sort: menu.sort }, "菜单 sort 最小值为 1");
|
|
152
121
|
}
|
|
153
|
-
|
|
154
122
|
if (pathSet.has(path)) {
|
|
155
123
|
hasError = true;
|
|
156
124
|
Logger.warn({ path: path }, "菜单 path 重复(严格模式禁止重复 path)");
|
|
157
125
|
continue;
|
|
158
126
|
}
|
|
159
|
-
|
|
160
127
|
pathSet.add(path);
|
|
161
128
|
}
|
|
162
|
-
|
|
163
129
|
if (hasError) {
|
|
164
130
|
throw new Error("菜单结构检查失败");
|
|
165
131
|
}
|
|
166
|
-
|
|
167
132
|
return filteredMenus;
|
|
168
133
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkPlugin(plugins: any[]): Promise<void>;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
2
|
import { omit } from "es-toolkit/object";
|
|
3
|
-
|
|
4
3
|
import { Logger } from "../lib/logger.js";
|
|
5
|
-
|
|
6
|
-
export async function checkPlugin(plugins: any[]): Promise<void> {
|
|
4
|
+
export async function checkPlugin(plugins) {
|
|
7
5
|
let hasError = false;
|
|
8
|
-
|
|
9
6
|
for (const plugin of plugins) {
|
|
10
7
|
try {
|
|
11
8
|
if (!isPlainObject(plugin)) {
|
|
@@ -13,35 +10,29 @@ export async function checkPlugin(plugins: any[]): Promise<void> {
|
|
|
13
10
|
hasError = true;
|
|
14
11
|
continue;
|
|
15
12
|
}
|
|
16
|
-
|
|
17
|
-
if (!Array.isArray((plugin as any).deps)) {
|
|
13
|
+
if (!Array.isArray(plugin.deps)) {
|
|
18
14
|
Logger.warn(omit(plugin, ["handler"]), "插件的 deps 属性必须是字符串数组");
|
|
19
15
|
hasError = true;
|
|
20
16
|
continue;
|
|
21
17
|
}
|
|
22
|
-
|
|
23
|
-
if ((plugin as any).deps.some((depItem: any) => typeof depItem !== "string")) {
|
|
18
|
+
if (plugin.deps.some((depItem) => typeof depItem !== "string")) {
|
|
24
19
|
Logger.warn(omit(plugin, ["handler"]), "插件的 deps 属性必须是字符串数组");
|
|
25
20
|
hasError = true;
|
|
26
21
|
}
|
|
27
|
-
|
|
28
|
-
if (typeof (plugin as any).handler !== "function") {
|
|
22
|
+
if (typeof plugin.handler !== "function") {
|
|
29
23
|
Logger.warn(omit(plugin, ["handler"]), "插件的 handler 属性必须是函数");
|
|
30
24
|
hasError = true;
|
|
31
25
|
continue;
|
|
32
26
|
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"插件解析失败"
|
|
40
|
-
);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
Logger.error({
|
|
30
|
+
err: error,
|
|
31
|
+
item: plugin
|
|
32
|
+
}, "插件解析失败");
|
|
41
33
|
hasError = true;
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
|
-
|
|
45
36
|
if (hasError) {
|
|
46
37
|
throw new Error("插件结构检查失败");
|
|
47
38
|
}
|
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import type { FieldDefinition } from "../types/validate.js";
|
|
2
|
-
import type { ScanFileResult } from "../utils/scanFiles.js";
|
|
3
|
-
|
|
4
1
|
import { Logger } from "../lib/logger.js";
|
|
5
|
-
|
|
6
2
|
/**
|
|
7
3
|
* 保留字段列表
|
|
8
4
|
*/
|
|
9
|
-
const RESERVED_FIELDS = ["id", "created_at", "updated_at", "deleted_at", "state"]
|
|
10
|
-
|
|
5
|
+
const RESERVED_FIELDS = ["id", "created_at", "updated_at", "deleted_at", "state"];
|
|
11
6
|
/**
|
|
12
7
|
* 允许的字段类型
|
|
13
8
|
*/
|
|
14
|
-
const FIELD_TYPES = ["string", "number", "text", "array_string", "array_text", "array_number_string", "array_number_text"]
|
|
15
|
-
|
|
9
|
+
const FIELD_TYPES = ["string", "number", "text", "array_string", "array_text", "array_number_string", "array_number_text"];
|
|
16
10
|
/**
|
|
17
11
|
* 允许的字段属性列表
|
|
18
12
|
*/
|
|
19
|
-
const ALLOWED_FIELD_PROPERTIES = ["name", "type", "min", "max", "default", "detail", "index", "unique", "nullable", "unsigned", "regexp"]
|
|
20
|
-
|
|
13
|
+
const ALLOWED_FIELD_PROPERTIES = ["name", "type", "min", "max", "default", "detail", "index", "unique", "nullable", "unsigned", "regexp"];
|
|
21
14
|
/**
|
|
22
15
|
* 小驼峰命名正则
|
|
23
16
|
* 可选:以下划线开头(用于特殊文件,如通用字段定义)
|
|
@@ -25,34 +18,28 @@ const ALLOWED_FIELD_PROPERTIES = ["name", "type", "min", "max", "default", "deta
|
|
|
25
18
|
* 示例:userTable、testCustomers、common
|
|
26
19
|
*/
|
|
27
20
|
const LOWER_CAMEL_CASE_REGEX = /^_?[a-z][a-z0-9]*(?:[A-Z][a-z0-9]*)*$/;
|
|
28
|
-
|
|
29
21
|
/**
|
|
30
22
|
* 字段名称正则
|
|
31
23
|
* 必须为中文、数字、字母、下划线、短横线、空格
|
|
32
24
|
*/
|
|
33
25
|
const FIELD_NAME_REGEX = /^[\u4e00-\u9fa5a-zA-Z0-9 _-]+$/;
|
|
34
|
-
|
|
35
26
|
/**
|
|
36
27
|
* VARCHAR 最大长度限制
|
|
37
28
|
*/
|
|
38
29
|
const MAX_VARCHAR_LENGTH = 65535;
|
|
39
|
-
|
|
40
30
|
/**
|
|
41
31
|
* 检查表定义文件
|
|
42
32
|
* @throws 当检查失败时抛出异常
|
|
43
33
|
*/
|
|
44
|
-
export async function checkTable(tables
|
|
34
|
+
export async function checkTable(tables) {
|
|
45
35
|
// 收集所有表文件
|
|
46
36
|
let hasError = false;
|
|
47
|
-
|
|
48
37
|
// 合并进行验证逻辑
|
|
49
38
|
for (const item of tables) {
|
|
50
39
|
if (item.type !== "table") {
|
|
51
40
|
continue;
|
|
52
41
|
}
|
|
53
|
-
|
|
54
42
|
const sourceName = typeof item.sourceName === "string" ? item.sourceName : "";
|
|
55
|
-
|
|
56
43
|
try {
|
|
57
44
|
const fileName = item.fileName;
|
|
58
45
|
const table = item.content || {};
|
|
@@ -62,7 +49,6 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
62
49
|
hasError = true;
|
|
63
50
|
continue;
|
|
64
51
|
}
|
|
65
|
-
|
|
66
52
|
// 检查 table 中的每个验证规则
|
|
67
53
|
for (const [colKey, fieldDef] of Object.entries(table)) {
|
|
68
54
|
if (typeof fieldDef !== "object" || fieldDef === null || Array.isArray(fieldDef)) {
|
|
@@ -70,24 +56,20 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
70
56
|
hasError = true;
|
|
71
57
|
continue;
|
|
72
58
|
}
|
|
73
|
-
|
|
74
59
|
// 检查是否使用了保留字段
|
|
75
|
-
if (RESERVED_FIELDS.includes(colKey
|
|
60
|
+
if (RESERVED_FIELDS.includes(colKey)) {
|
|
76
61
|
Logger.warn(`${sourceName}表 ${fileName} 文件包含保留字段 ${colKey},` + `不能在表定义中使用以下字段: ${RESERVED_FIELDS.join(", ")}`);
|
|
77
62
|
hasError = true;
|
|
78
63
|
}
|
|
79
|
-
|
|
80
64
|
// 直接使用字段对象
|
|
81
|
-
const field = fieldDef
|
|
82
|
-
|
|
65
|
+
const field = fieldDef;
|
|
83
66
|
// 检查是否存在非法属性
|
|
84
67
|
const fieldKeys = Object.keys(field);
|
|
85
|
-
const illegalProps = fieldKeys.filter((key) => !ALLOWED_FIELD_PROPERTIES.includes(key
|
|
68
|
+
const illegalProps = fieldKeys.filter((key) => !ALLOWED_FIELD_PROPERTIES.includes(key));
|
|
86
69
|
if (illegalProps.length > 0) {
|
|
87
70
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 包含非法属性: ${illegalProps.join(", ")},` + `允许的属性为: ${ALLOWED_FIELD_PROPERTIES.join(", ")}`);
|
|
88
71
|
hasError = true;
|
|
89
72
|
}
|
|
90
|
-
|
|
91
73
|
// 检查必填字段:name, type
|
|
92
74
|
if (!field.name || typeof field.name !== "string") {
|
|
93
75
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 缺少必填字段 name 或类型错误`);
|
|
@@ -99,7 +81,6 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
99
81
|
hasError = true;
|
|
100
82
|
continue;
|
|
101
83
|
}
|
|
102
|
-
|
|
103
84
|
// 检查可选字段的类型
|
|
104
85
|
if (field.min !== undefined && !(field.min === null || typeof field.min === "number")) {
|
|
105
86
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 字段 min 类型错误,必须为 null 或数字`);
|
|
@@ -133,33 +114,27 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
133
114
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 字段 regexp 类型错误,必须为 null 或字符串`);
|
|
134
115
|
hasError = true;
|
|
135
116
|
}
|
|
136
|
-
|
|
137
117
|
const { name: fieldName, type: fieldType, min: fieldMin, max: fieldMax, default: fieldDefault } = field;
|
|
138
|
-
|
|
139
118
|
// 字段名称必须为中文、数字、字母、下划线、短横线、空格
|
|
140
119
|
if (!FIELD_NAME_REGEX.test(fieldName)) {
|
|
141
120
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 字段名称 "${fieldName}" 格式错误,` + `必须为中文、数字、字母、下划线、短横线、空格`);
|
|
142
121
|
hasError = true;
|
|
143
122
|
}
|
|
144
|
-
|
|
145
123
|
// 字段类型必须为string,number,text,array_string,array_text之一
|
|
146
|
-
if (!FIELD_TYPES.includes(fieldType
|
|
124
|
+
if (!FIELD_TYPES.includes(fieldType)) {
|
|
147
125
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 字段类型 "${fieldType}" 格式错误,` + `必须为${FIELD_TYPES.join("、")}之一`);
|
|
148
126
|
hasError = true;
|
|
149
127
|
}
|
|
150
|
-
|
|
151
128
|
// unsigned 仅对 number 类型有效(且仅 MySQL 语义上生效)
|
|
152
129
|
if (fieldType !== "number" && field.unsigned !== undefined) {
|
|
153
130
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 字段类型为 ${fieldType},不允许设置 unsigned(仅 number 类型有效)`);
|
|
154
131
|
hasError = true;
|
|
155
132
|
}
|
|
156
|
-
|
|
157
133
|
// 约束:unique 与 index 不能同时为 true(否则会重复索引),必须阻断启动。
|
|
158
134
|
if (field.unique === true && field.index === true) {
|
|
159
135
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 同时设置了 unique=true 和 index=true,` + `unique 和 index 不能同时设置,请删除其一(否则会创建重复索引)`);
|
|
160
136
|
hasError = true;
|
|
161
137
|
}
|
|
162
|
-
|
|
163
138
|
// 约束:当最小值与最大值均为数字时,要求最小值 <= 最大值
|
|
164
139
|
if (fieldMin !== undefined && fieldMax !== undefined && fieldMin !== null && fieldMax !== null) {
|
|
165
140
|
if (fieldMin > fieldMax) {
|
|
@@ -167,7 +142,6 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
167
142
|
hasError = true;
|
|
168
143
|
}
|
|
169
144
|
}
|
|
170
|
-
|
|
171
145
|
// 类型联动校验 + 默认值规则
|
|
172
146
|
if (fieldType === "text" || fieldType === "array_text" || fieldType === "array_number_text") {
|
|
173
147
|
// text / array_text / array_number_text:min/max 必须为 null,默认值必须为 null,且不支持索引/唯一约束
|
|
@@ -183,7 +157,6 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
183
157
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 为 ${fieldType} 类型,默认值必须为 null,当前为 "${fieldDefault}"`);
|
|
184
158
|
hasError = true;
|
|
185
159
|
}
|
|
186
|
-
|
|
187
160
|
if (field.index === true) {
|
|
188
161
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 为 ${fieldType} 类型,不支持创建索引(index=true 无效)`);
|
|
189
162
|
hasError = true;
|
|
@@ -192,15 +165,18 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
192
165
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 为 ${fieldType} 类型,不支持唯一约束(unique=true 无效)`);
|
|
193
166
|
hasError = true;
|
|
194
167
|
}
|
|
195
|
-
}
|
|
168
|
+
}
|
|
169
|
+
else if (fieldType === "string" || fieldType === "array_string" || fieldType === "array_number_string") {
|
|
196
170
|
if (fieldMax !== undefined && (fieldMax === null || typeof fieldMax !== "number")) {
|
|
197
171
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 为 ${fieldType} 类型,` + `最大长度必须为数字,当前为 "${fieldMax}"`);
|
|
198
172
|
hasError = true;
|
|
199
|
-
}
|
|
173
|
+
}
|
|
174
|
+
else if (fieldMax !== undefined && fieldMax > MAX_VARCHAR_LENGTH) {
|
|
200
175
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 最大长度 ${fieldMax} 越界,` + `${fieldType} 类型长度必须在 1..${MAX_VARCHAR_LENGTH} 范围内`);
|
|
201
176
|
hasError = true;
|
|
202
177
|
}
|
|
203
|
-
}
|
|
178
|
+
}
|
|
179
|
+
else if (fieldType === "number") {
|
|
204
180
|
// number 类型:default 如果存在,必须为 null 或 number
|
|
205
181
|
if (fieldDefault !== undefined && fieldDefault !== null && typeof fieldDefault !== "number") {
|
|
206
182
|
Logger.warn(`${sourceName}表 ${fileName} 文件 ${colKey} 为 number 类型,` + `默认值必须为数字或 null,当前为 "${fieldDefault}"`);
|
|
@@ -208,12 +184,12 @@ export async function checkTable(tables: ScanFileResult[]): Promise<void> {
|
|
|
208
184
|
}
|
|
209
185
|
}
|
|
210
186
|
}
|
|
211
|
-
}
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
212
189
|
Logger.error(`${sourceName}表 ${item.fileName} 解析失败`, error);
|
|
213
190
|
hasError = true;
|
|
214
191
|
}
|
|
215
192
|
}
|
|
216
|
-
|
|
217
193
|
if (hasError) {
|
|
218
194
|
throw new Error("表结构检查失败");
|
|
219
195
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 预定义的默认字段(API fields 的 @ 引用)
|
|
3
3
|
*/
|
|
4
|
-
export const presetFields
|
|
4
|
+
export const presetFields = {
|
|
5
5
|
"@id": { name: "ID", type: "number", min: 1, max: null },
|
|
6
6
|
"@page": { name: "页码", type: "number", min: 1, max: 9999, default: 1 },
|
|
7
7
|
"@limit": { name: "每页数量", type: "number", min: 1, max: 100, default: 30 },
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内置正则表达式别名
|
|
3
|
+
* 用于表单验证和数据校验
|
|
4
|
+
* 命名规范:小驼峰格式
|
|
5
|
+
*/
|
|
6
|
+
export declare const RegexAliases: {
|
|
7
|
+
/** 正整数(不含0) */
|
|
8
|
+
readonly number: "^\\d+$";
|
|
9
|
+
/** 整数(含负数) */
|
|
10
|
+
readonly integer: "^-?\\d+$";
|
|
11
|
+
/** 浮点数 */
|
|
12
|
+
readonly float: "^-?\\d+(\\.\\d+)?$";
|
|
13
|
+
/** 正整数(不含0) */
|
|
14
|
+
readonly positive: "^[1-9]\\d*$";
|
|
15
|
+
/** 负整数 */
|
|
16
|
+
readonly negative: "^-\\d+$";
|
|
17
|
+
/** 零 */
|
|
18
|
+
readonly zero: "^0$";
|
|
19
|
+
/** 纯字母 */
|
|
20
|
+
readonly word: "^[a-zA-Z]+$";
|
|
21
|
+
/** 字母和数字 */
|
|
22
|
+
readonly alphanumeric: "^[a-zA-Z0-9]+$";
|
|
23
|
+
/** 字母、数字和下划线 */
|
|
24
|
+
readonly alphanumeric_: "^[a-zA-Z0-9_]+$";
|
|
25
|
+
/** 字母、数字、下划线和短横线 */
|
|
26
|
+
readonly alphanumericDash_: "^[a-zA-Z0-9_-]+$";
|
|
27
|
+
/** 小写字母 */
|
|
28
|
+
readonly lowercase: "^[a-z]+$";
|
|
29
|
+
/** 大写字母 */
|
|
30
|
+
readonly uppercase: "^[A-Z]+$";
|
|
31
|
+
/** 纯中文 */
|
|
32
|
+
readonly chinese: "^[\\u4e00-\\u9fa5]+$";
|
|
33
|
+
/** 中文和字母 */
|
|
34
|
+
readonly chineseWord: "^[\\u4e00-\\u9fa5a-zA-Z]+$";
|
|
35
|
+
/** 邮箱地址 */
|
|
36
|
+
readonly email: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
|
37
|
+
/** 中国大陆手机号 */
|
|
38
|
+
readonly phone: "^1[3-9]\\d{9}$";
|
|
39
|
+
/** 固定电话(区号-号码) */
|
|
40
|
+
readonly telephone: "^0\\d{2,3}-?\\d{7,8}$";
|
|
41
|
+
/** URL 地址 */
|
|
42
|
+
readonly url: "^https?://";
|
|
43
|
+
/** IPv4 地址 */
|
|
44
|
+
readonly ip: "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";
|
|
45
|
+
/** IPv6 地址 */
|
|
46
|
+
readonly ipv6: "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$";
|
|
47
|
+
/** 域名 */
|
|
48
|
+
readonly domain: "^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$";
|
|
49
|
+
/** UUID */
|
|
50
|
+
readonly uuid: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
|
|
51
|
+
/** 十六进制字符串 */
|
|
52
|
+
readonly hex: "^[0-9a-fA-F]+$";
|
|
53
|
+
/** Base64 编码 */
|
|
54
|
+
readonly base64: "^[A-Za-z0-9+/=]+$";
|
|
55
|
+
/** MD5 哈希 */
|
|
56
|
+
readonly md5: "^[a-f0-9]{32}$";
|
|
57
|
+
/** SHA1 哈希 */
|
|
58
|
+
readonly sha1: "^[a-f0-9]{40}$";
|
|
59
|
+
/** SHA256 哈希 */
|
|
60
|
+
readonly sha256: "^[a-f0-9]{64}$";
|
|
61
|
+
/** 日期 YYYY-MM-DD */
|
|
62
|
+
readonly date: "^\\d{4}-\\d{2}-\\d{2}$";
|
|
63
|
+
/** 时间 HH:MM:SS */
|
|
64
|
+
readonly time: "^\\d{2}:\\d{2}:\\d{2}$";
|
|
65
|
+
/** ISO 日期时间 */
|
|
66
|
+
readonly datetime: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}";
|
|
67
|
+
/** 年份 */
|
|
68
|
+
readonly year: "^\\d{4}$";
|
|
69
|
+
/** 月份 01-12 */
|
|
70
|
+
readonly month: "^(0[1-9]|1[0-2])$";
|
|
71
|
+
/** 日期 01-31 */
|
|
72
|
+
readonly day: "^(0[1-9]|[12]\\d|3[01])$";
|
|
73
|
+
/** 变量名 */
|
|
74
|
+
readonly variable: "^[a-zA-Z_][a-zA-Z0-9_]*$";
|
|
75
|
+
/** 常量名(全大写) */
|
|
76
|
+
readonly constant: "^[A-Z][A-Z0-9_]*$";
|
|
77
|
+
/** 包名(小写+连字符) */
|
|
78
|
+
readonly package: "^[a-z][a-z0-9-]*$";
|
|
79
|
+
/** 中国身份证号(18位) */
|
|
80
|
+
readonly idCard: "^\\d{17}[\\dXx]$";
|
|
81
|
+
/** 护照号 */
|
|
82
|
+
readonly passport: "^[a-zA-Z0-9]{5,17}$";
|
|
83
|
+
/** 银行卡号(16-19位数字) */
|
|
84
|
+
readonly bankCard: "^\\d{16,19}$";
|
|
85
|
+
/** 微信号(6-20位,字母开头,可包含字母、数字、下划线、减号) */
|
|
86
|
+
readonly wechat: "^[a-zA-Z][a-zA-Z0-9_-]{5,19}$";
|
|
87
|
+
/** QQ号(5-11位数字,首位非0) */
|
|
88
|
+
readonly qq: "^[1-9]\\d{4,10}$";
|
|
89
|
+
/** 支付宝账号(手机号或邮箱) */
|
|
90
|
+
readonly alipay: "^(1[3-9]\\d{9}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$";
|
|
91
|
+
/** 用户名(4-20位,字母开头,可包含字母、数字、下划线) */
|
|
92
|
+
readonly username: "^[a-zA-Z][a-zA-Z0-9_]{3,19}$";
|
|
93
|
+
/** 昵称(2-20位,支持中文、字母、数字) */
|
|
94
|
+
readonly nickname: "^[\\u4e00-\\u9fa5a-zA-Z0-9]{2,20}$";
|
|
95
|
+
/** 弱密码(至少6位) */
|
|
96
|
+
readonly passwordWeak: "^.{6,}$";
|
|
97
|
+
/** 中等密码(至少8位,包含字母和数字) */
|
|
98
|
+
readonly passwordMedium: "^(?=.*[a-zA-Z])(?=.*\\d).{8,}$";
|
|
99
|
+
/** 强密码(至少8位,包含大小写字母、数字和特殊字符) */
|
|
100
|
+
readonly passwordStrong: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*]).{8,}$";
|
|
101
|
+
/** 车牌号(新能源+普通) */
|
|
102
|
+
readonly licensePlate: "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$";
|
|
103
|
+
/** 邮政编码 */
|
|
104
|
+
readonly postalCode: "^\\d{6}$";
|
|
105
|
+
/** 版本号(语义化版本) */
|
|
106
|
+
readonly semver: "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?(\\+[a-zA-Z0-9.]+)?$";
|
|
107
|
+
/** 颜色值(十六进制) */
|
|
108
|
+
readonly colorHex: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$";
|
|
109
|
+
/** 空字符串 */
|
|
110
|
+
readonly empty: "^$";
|
|
111
|
+
/** 非空 */
|
|
112
|
+
readonly notempty: ".+";
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* 正则别名类型
|
|
116
|
+
*/
|
|
117
|
+
export type RegexAliasName = keyof typeof RegexAliases;
|
|
118
|
+
/**
|
|
119
|
+
* 获取正则表达式字符串
|
|
120
|
+
* @param name 正则别名(以 @ 开头)或自定义正则字符串
|
|
121
|
+
* @returns 正则表达式字符串
|
|
122
|
+
*/
|
|
123
|
+
export declare function getRegex(name: string): string;
|
|
124
|
+
/**
|
|
125
|
+
* 获取编译后的正则表达式对象(带缓存)
|
|
126
|
+
* @param pattern 正则别名或正则字符串
|
|
127
|
+
* @param flags 正则标志(如 'i', 'g')
|
|
128
|
+
* @returns 编译后的 RegExp 对象
|
|
129
|
+
*/
|
|
130
|
+
export declare function getCompiledRegex(pattern: string, flags?: string): RegExp;
|
|
131
|
+
/**
|
|
132
|
+
* 验证值是否匹配正则(使用缓存)
|
|
133
|
+
* @param value 要验证的值
|
|
134
|
+
* @param pattern 正则别名或正则字符串
|
|
135
|
+
* @returns 是否匹配
|
|
136
|
+
*/
|
|
137
|
+
export declare function matchRegex(value: string, pattern: string): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* 清除正则缓存
|
|
140
|
+
*/
|
|
141
|
+
export declare function clearRegexCache(): void;
|
|
142
|
+
/**
|
|
143
|
+
* 获取缓存大小
|
|
144
|
+
*/
|
|
145
|
+
export declare function getRegexCacheSize(): number;
|