@dangao/bun-server 1.7.0 → 1.8.0
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 +196 -19
- package/dist/cache/cache-module.d.ts +18 -0
- package/dist/cache/cache-module.d.ts.map +1 -1
- package/dist/cache/index.d.ts +3 -1
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/interceptors.d.ts +41 -0
- package/dist/cache/interceptors.d.ts.map +1 -0
- package/dist/cache/service-proxy.d.ts +62 -0
- package/dist/cache/service-proxy.d.ts.map +1 -0
- package/dist/controller/controller.d.ts +8 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts +5 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/di/container.d.ts +18 -1
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +2 -2
- package/dist/di/index.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts +17 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/di/types.d.ts +22 -0
- package/dist/di/types.d.ts.map +1 -1
- package/dist/events/decorators.d.ts +52 -0
- package/dist/events/decorators.d.ts.map +1 -0
- package/dist/events/event-module.d.ts +97 -0
- package/dist/events/event-module.d.ts.map +1 -0
- package/dist/events/index.d.ts +5 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/service.d.ts +76 -0
- package/dist/events/service.d.ts.map +1 -0
- package/dist/events/types.d.ts +184 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4641 -2840
- package/dist/security/filter.d.ts +23 -0
- package/dist/security/filter.d.ts.map +1 -1
- package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
- package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
- package/dist/security/guards/builtin/index.d.ts +3 -0
- package/dist/security/guards/builtin/index.d.ts.map +1 -0
- package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
- package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
- package/dist/security/guards/decorators.d.ts +50 -0
- package/dist/security/guards/decorators.d.ts.map +1 -0
- package/dist/security/guards/execution-context.d.ts +56 -0
- package/dist/security/guards/execution-context.d.ts.map +1 -0
- package/dist/security/guards/guard-registry.d.ts +67 -0
- package/dist/security/guards/guard-registry.d.ts.map +1 -0
- package/dist/security/guards/index.d.ts +7 -0
- package/dist/security/guards/index.d.ts.map +1 -0
- package/dist/security/guards/reflector.d.ts +57 -0
- package/dist/security/guards/reflector.d.ts.map +1 -0
- package/dist/security/guards/types.d.ts +126 -0
- package/dist/security/guards/types.d.ts.map +1 -0
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/security-module.d.ts +20 -0
- package/dist/security/security-module.d.ts.map +1 -1
- package/dist/validation/class-validator.d.ts +108 -0
- package/dist/validation/class-validator.d.ts.map +1 -0
- package/dist/validation/custom-validator.d.ts +130 -0
- package/dist/validation/custom-validator.d.ts.map +1 -0
- package/dist/validation/errors.d.ts +22 -2
- package/dist/validation/errors.d.ts.map +1 -1
- package/dist/validation/index.d.ts +7 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/rules/array.d.ts +33 -0
- package/dist/validation/rules/array.d.ts.map +1 -0
- package/dist/validation/rules/common.d.ts +90 -0
- package/dist/validation/rules/common.d.ts.map +1 -0
- package/dist/validation/rules/conditional.d.ts +30 -0
- package/dist/validation/rules/conditional.d.ts.map +1 -0
- package/dist/validation/rules/index.d.ts +5 -0
- package/dist/validation/rules/index.d.ts.map +1 -0
- package/dist/validation/rules/object.d.ts +30 -0
- package/dist/validation/rules/object.d.ts.map +1 -0
- package/dist/validation/types.d.ts +52 -1
- package/dist/validation/types.d.ts.map +1 -1
- package/docs/events.md +494 -0
- package/docs/guards.md +376 -0
- package/docs/guide.md +309 -1
- package/docs/request-lifecycle.md +444 -0
- package/docs/symbol-interface-pattern.md +431 -0
- package/docs/validation.md +407 -0
- package/docs/zh/events.md +494 -0
- package/docs/zh/guards.md +376 -0
- package/docs/zh/guide.md +309 -1
- package/docs/zh/request-lifecycle.md +444 -0
- package/docs/zh/symbol-interface-pattern.md +431 -0
- package/docs/zh/validation.md +407 -0
- package/package.json +1 -1
- package/src/cache/cache-module.ts +37 -0
- package/src/cache/index.ts +16 -1
- package/src/cache/interceptors.ts +295 -0
- package/src/cache/service-proxy.ts +219 -0
- package/src/controller/controller.ts +30 -6
- package/src/core/application.ts +25 -1
- package/src/di/container.ts +57 -7
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +17 -2
- package/src/di/module-registry.ts +39 -0
- package/src/di/types.ts +29 -0
- package/src/events/decorators.ts +103 -0
- package/src/events/event-module.ts +272 -0
- package/src/events/index.ts +32 -0
- package/src/events/service.ts +352 -0
- package/src/events/types.ts +223 -0
- package/src/index.ts +140 -1
- package/src/security/filter.ts +88 -8
- package/src/security/guards/builtin/auth-guard.ts +68 -0
- package/src/security/guards/builtin/index.ts +3 -0
- package/src/security/guards/builtin/roles-guard.ts +165 -0
- package/src/security/guards/decorators.ts +124 -0
- package/src/security/guards/execution-context.ts +152 -0
- package/src/security/guards/guard-registry.ts +164 -0
- package/src/security/guards/index.ts +7 -0
- package/src/security/guards/reflector.ts +99 -0
- package/src/security/guards/types.ts +144 -0
- package/src/security/index.ts +1 -0
- package/src/security/security-module.ts +72 -2
- package/src/validation/class-validator.ts +322 -0
- package/src/validation/custom-validator.ts +289 -0
- package/src/validation/errors.ts +50 -2
- package/src/validation/index.ts +103 -1
- package/src/validation/rules/array.ts +118 -0
- package/src/validation/rules/common.ts +286 -0
- package/src/validation/rules/conditional.ts +52 -0
- package/src/validation/rules/index.ts +51 -0
- package/src/validation/rules/object.ts +86 -0
- package/src/validation/types.ts +61 -1
- package/tests/cache/cache-decorators.test.ts +284 -0
- package/tests/controller/path-combination.test.ts +353 -0
- package/tests/di/global-module.test.ts +487 -0
- package/tests/events/event-decorators.test.ts +173 -0
- package/tests/events/event-emitter.test.ts +373 -0
- package/tests/events/event-module.test.ts +373 -0
- package/tests/security/guards/guards-integration.test.ts +371 -0
- package/tests/security/guards/guards.test.ts +775 -0
- package/tests/security/security-module.test.ts +2 -2
- package/tests/validation/class-validator.test.ts +349 -0
- package/tests/validation/custom-validator.test.ts +335 -0
- package/tests/validation/rules.test.ts +543 -0
package/src/validation/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// 基础装饰器
|
|
1
2
|
export {
|
|
2
3
|
Validate,
|
|
3
4
|
IsString,
|
|
@@ -7,8 +8,109 @@ export {
|
|
|
7
8
|
MinLength,
|
|
8
9
|
getValidationMetadata,
|
|
9
10
|
} from './decorators';
|
|
10
|
-
|
|
11
|
+
|
|
12
|
+
// 类型定义
|
|
13
|
+
export type {
|
|
14
|
+
ValidationRuleDefinition,
|
|
15
|
+
ValidationMetadata,
|
|
16
|
+
ClassValidationMetadata,
|
|
17
|
+
ValidationOptions,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
// 验证器
|
|
11
21
|
export { validateParameters } from './validator';
|
|
22
|
+
|
|
23
|
+
// 错误处理
|
|
12
24
|
export { ValidationError, type ValidationIssue } from './errors';
|
|
13
25
|
|
|
26
|
+
// 验证规则 - 对象
|
|
27
|
+
export {
|
|
28
|
+
IsObject,
|
|
29
|
+
IsNotEmpty,
|
|
30
|
+
IsNotEmptyObject,
|
|
31
|
+
ValidateNested,
|
|
32
|
+
type ObjectRuleOptions,
|
|
33
|
+
type ValidateNestedOptions,
|
|
34
|
+
} from './rules/object';
|
|
35
|
+
|
|
36
|
+
// 验证规则 - 数组
|
|
37
|
+
export {
|
|
38
|
+
IsArray,
|
|
39
|
+
ArrayMinSize,
|
|
40
|
+
ArrayMaxSize,
|
|
41
|
+
ArrayUnique,
|
|
42
|
+
ArrayContains,
|
|
43
|
+
ArrayNotContains,
|
|
44
|
+
ArrayNotEmpty,
|
|
45
|
+
type ArrayRuleOptions,
|
|
46
|
+
} from './rules/array';
|
|
47
|
+
|
|
48
|
+
// 验证规则 - 通用
|
|
49
|
+
export {
|
|
50
|
+
IsBoolean,
|
|
51
|
+
IsInt,
|
|
52
|
+
IsPositive,
|
|
53
|
+
IsNegative,
|
|
54
|
+
Min,
|
|
55
|
+
Max,
|
|
56
|
+
IsDate,
|
|
57
|
+
IsUUID,
|
|
58
|
+
Length,
|
|
59
|
+
MaxLength,
|
|
60
|
+
Matches,
|
|
61
|
+
IsIn,
|
|
62
|
+
IsNotIn,
|
|
63
|
+
IsUrl,
|
|
64
|
+
IsJSON,
|
|
65
|
+
Equals,
|
|
66
|
+
NotEquals,
|
|
67
|
+
IsDefined,
|
|
68
|
+
IsAlphanumeric,
|
|
69
|
+
IsAlpha,
|
|
70
|
+
IsNumberString,
|
|
71
|
+
type RuleOptions,
|
|
72
|
+
type UUIDVersion,
|
|
73
|
+
} from './rules/common';
|
|
74
|
+
|
|
75
|
+
// 验证规则 - 条件和转换
|
|
76
|
+
export {
|
|
77
|
+
ValidateIf,
|
|
78
|
+
Transform,
|
|
79
|
+
type ConditionalRuleOptions,
|
|
80
|
+
} from './rules/conditional';
|
|
81
|
+
|
|
82
|
+
// 自定义验证器
|
|
83
|
+
export {
|
|
84
|
+
createCustomValidator,
|
|
85
|
+
createSimpleValidator,
|
|
86
|
+
createRegexValidator,
|
|
87
|
+
// 内置扩展验证器
|
|
88
|
+
IsPhoneNumber,
|
|
89
|
+
IsIdCard,
|
|
90
|
+
IsIPv4,
|
|
91
|
+
IsPort,
|
|
92
|
+
IsPostalCode,
|
|
93
|
+
IsCreditCard,
|
|
94
|
+
IsHexColor,
|
|
95
|
+
IsMacAddress,
|
|
96
|
+
IsSemVer,
|
|
97
|
+
IsDivisibleBy,
|
|
98
|
+
IsBetween,
|
|
99
|
+
Contains,
|
|
100
|
+
NotContains,
|
|
101
|
+
type CustomValidatorOptions,
|
|
102
|
+
} from './custom-validator';
|
|
103
|
+
|
|
104
|
+
// 类级别验证
|
|
105
|
+
export {
|
|
106
|
+
ValidateClass,
|
|
107
|
+
Property,
|
|
108
|
+
NestedProperty,
|
|
109
|
+
ArrayNestedProperty,
|
|
110
|
+
validateObject,
|
|
111
|
+
validateObjectSync,
|
|
112
|
+
getClassValidationMetadata,
|
|
113
|
+
isValidateClass,
|
|
114
|
+
} from './class-validator';
|
|
115
|
+
|
|
14
116
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { ValidationRuleDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface ArrayRuleOptions {
|
|
4
|
+
message?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 验证值是否为数组
|
|
9
|
+
*/
|
|
10
|
+
export function IsArray(options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
11
|
+
return {
|
|
12
|
+
name: 'isArray',
|
|
13
|
+
message: options.message ?? '必须是数组',
|
|
14
|
+
validate: (value) => Array.isArray(value),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 验证数组最小长度
|
|
20
|
+
*/
|
|
21
|
+
export function ArrayMinSize(min: number, options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
22
|
+
return {
|
|
23
|
+
name: 'arrayMinSize',
|
|
24
|
+
message: options.message ?? `数组长度不能小于 ${min}`,
|
|
25
|
+
validate: (value) => Array.isArray(value) && value.length >= min,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 验证数组最大长度
|
|
31
|
+
*/
|
|
32
|
+
export function ArrayMaxSize(max: number, options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
33
|
+
return {
|
|
34
|
+
name: 'arrayMaxSize',
|
|
35
|
+
message: options.message ?? `数组长度不能大于 ${max}`,
|
|
36
|
+
validate: (value) => Array.isArray(value) && value.length <= max,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 验证数组元素是否唯一
|
|
42
|
+
*/
|
|
43
|
+
export function ArrayUnique(options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
44
|
+
return {
|
|
45
|
+
name: 'arrayUnique',
|
|
46
|
+
message: options.message ?? '数组元素必须唯一',
|
|
47
|
+
validate: (value) => {
|
|
48
|
+
if (!Array.isArray(value)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
const seen = new Set();
|
|
52
|
+
for (const item of value) {
|
|
53
|
+
// 对于对象,使用 JSON.stringify 进行比较
|
|
54
|
+
const key = typeof item === 'object' ? JSON.stringify(item) : item;
|
|
55
|
+
if (seen.has(key)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
seen.add(key);
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 验证数组是否包含指定的值
|
|
67
|
+
*/
|
|
68
|
+
export function ArrayContains(values: unknown[], options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
69
|
+
return {
|
|
70
|
+
name: 'arrayContains',
|
|
71
|
+
message: options.message ?? `数组必须包含 ${values.join(', ')}`,
|
|
72
|
+
validate: (value) => {
|
|
73
|
+
if (!Array.isArray(value)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return values.every((v) => {
|
|
77
|
+
if (typeof v === 'object') {
|
|
78
|
+
const vStr = JSON.stringify(v);
|
|
79
|
+
return value.some((item) => JSON.stringify(item) === vStr);
|
|
80
|
+
}
|
|
81
|
+
return value.includes(v);
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 验证数组是否不包含指定的值
|
|
89
|
+
*/
|
|
90
|
+
export function ArrayNotContains(values: unknown[], options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
91
|
+
return {
|
|
92
|
+
name: 'arrayNotContains',
|
|
93
|
+
message: options.message ?? `数组不能包含 ${values.join(', ')}`,
|
|
94
|
+
validate: (value) => {
|
|
95
|
+
if (!Array.isArray(value)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return values.every((v) => {
|
|
99
|
+
if (typeof v === 'object') {
|
|
100
|
+
const vStr = JSON.stringify(v);
|
|
101
|
+
return !value.some((item) => JSON.stringify(item) === vStr);
|
|
102
|
+
}
|
|
103
|
+
return !value.includes(v);
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 验证数组是否为非空数组
|
|
111
|
+
*/
|
|
112
|
+
export function ArrayNotEmpty(options: ArrayRuleOptions = {}): ValidationRuleDefinition {
|
|
113
|
+
return {
|
|
114
|
+
name: 'arrayNotEmpty',
|
|
115
|
+
message: options.message ?? '数组不能为空',
|
|
116
|
+
validate: (value) => Array.isArray(value) && value.length > 0,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import type { ValidationRuleDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface RuleOptions {
|
|
4
|
+
message?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 验证值是否为布尔值
|
|
9
|
+
*/
|
|
10
|
+
export function IsBoolean(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
11
|
+
return {
|
|
12
|
+
name: 'isBoolean',
|
|
13
|
+
message: options.message ?? '必须是布尔值',
|
|
14
|
+
validate: (value) => typeof value === 'boolean',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 验证值是否为整数
|
|
20
|
+
*/
|
|
21
|
+
export function IsInt(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
22
|
+
return {
|
|
23
|
+
name: 'isInt',
|
|
24
|
+
message: options.message ?? '必须是整数',
|
|
25
|
+
validate: (value) => typeof value === 'number' && Number.isInteger(value),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 验证值是否为正数
|
|
31
|
+
*/
|
|
32
|
+
export function IsPositive(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
33
|
+
return {
|
|
34
|
+
name: 'isPositive',
|
|
35
|
+
message: options.message ?? '必须是正数',
|
|
36
|
+
validate: (value) => typeof value === 'number' && value > 0,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 验证值是否为负数
|
|
42
|
+
*/
|
|
43
|
+
export function IsNegative(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
44
|
+
return {
|
|
45
|
+
name: 'isNegative',
|
|
46
|
+
message: options.message ?? '必须是负数',
|
|
47
|
+
validate: (value) => typeof value === 'number' && value < 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 验证数字最小值
|
|
53
|
+
*/
|
|
54
|
+
export function Min(min: number, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
55
|
+
return {
|
|
56
|
+
name: 'min',
|
|
57
|
+
message: options.message ?? `不能小于 ${min}`,
|
|
58
|
+
validate: (value) => typeof value === 'number' && value >= min,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 验证数字最大值
|
|
64
|
+
*/
|
|
65
|
+
export function Max(max: number, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
66
|
+
return {
|
|
67
|
+
name: 'max',
|
|
68
|
+
message: options.message ?? `不能大于 ${max}`,
|
|
69
|
+
validate: (value) => typeof value === 'number' && value <= max,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 验证值是否为日期
|
|
75
|
+
*/
|
|
76
|
+
export function IsDate(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
77
|
+
return {
|
|
78
|
+
name: 'isDate',
|
|
79
|
+
message: options.message ?? '必须是有效的日期',
|
|
80
|
+
validate: (value) => {
|
|
81
|
+
if (value instanceof Date) {
|
|
82
|
+
return !Number.isNaN(value.getTime());
|
|
83
|
+
}
|
|
84
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
85
|
+
const date = new Date(value);
|
|
86
|
+
return !Number.isNaN(date.getTime());
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type UUIDVersion = '3' | '4' | '5' | 'all';
|
|
94
|
+
|
|
95
|
+
const UUID_PATTERNS: Record<UUIDVersion, RegExp> = {
|
|
96
|
+
'3': /^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
97
|
+
'4': /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
98
|
+
'5': /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
99
|
+
'all': /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 验证值是否为 UUID
|
|
104
|
+
*/
|
|
105
|
+
export function IsUUID(version: UUIDVersion = 'all', options: RuleOptions = {}): ValidationRuleDefinition {
|
|
106
|
+
return {
|
|
107
|
+
name: 'isUUID',
|
|
108
|
+
message: options.message ?? `必须是有效的 UUID${version !== 'all' ? ` (v${version})` : ''}`,
|
|
109
|
+
validate: (value) => typeof value === 'string' && UUID_PATTERNS[version].test(value),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 验证字符串长度
|
|
115
|
+
*/
|
|
116
|
+
export function Length(min: number, max?: number, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
117
|
+
const maxMsg = max !== undefined ? ` 且不能大于 ${max}` : '';
|
|
118
|
+
return {
|
|
119
|
+
name: 'length',
|
|
120
|
+
message: options.message ?? `长度不能小于 ${min}${maxMsg}`,
|
|
121
|
+
validate: (value) => {
|
|
122
|
+
if (typeof value !== 'string') {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (value.length < min) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
if (max !== undefined && value.length > max) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 验证字符串最大长度
|
|
138
|
+
*/
|
|
139
|
+
export function MaxLength(max: number, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
140
|
+
return {
|
|
141
|
+
name: 'maxLength',
|
|
142
|
+
message: options.message ?? `长度不能大于 ${max}`,
|
|
143
|
+
validate: (value) => typeof value === 'string' && value.length <= max,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 验证值是否匹配正则表达式
|
|
149
|
+
*/
|
|
150
|
+
export function Matches(pattern: RegExp, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
151
|
+
return {
|
|
152
|
+
name: 'matches',
|
|
153
|
+
message: options.message ?? `必须匹配格式 ${pattern.toString()}`,
|
|
154
|
+
validate: (value) => typeof value === 'string' && pattern.test(value),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 验证值是否在指定列表中
|
|
160
|
+
*/
|
|
161
|
+
export function IsIn(values: unknown[], options: RuleOptions = {}): ValidationRuleDefinition {
|
|
162
|
+
return {
|
|
163
|
+
name: 'isIn',
|
|
164
|
+
message: options.message ?? `必须是以下值之一: ${values.join(', ')}`,
|
|
165
|
+
validate: (value) => values.includes(value),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 验证值是否不在指定列表中
|
|
171
|
+
*/
|
|
172
|
+
export function IsNotIn(values: unknown[], options: RuleOptions = {}): ValidationRuleDefinition {
|
|
173
|
+
return {
|
|
174
|
+
name: 'isNotIn',
|
|
175
|
+
message: options.message ?? `不能是以下值之一: ${values.join(', ')}`,
|
|
176
|
+
validate: (value) => !values.includes(value),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 验证值是否为 URL
|
|
182
|
+
*/
|
|
183
|
+
export function IsUrl(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
184
|
+
return {
|
|
185
|
+
name: 'isUrl',
|
|
186
|
+
message: options.message ?? '必须是有效的 URL',
|
|
187
|
+
validate: (value) => {
|
|
188
|
+
if (typeof value !== 'string') {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
new URL(value);
|
|
193
|
+
return true;
|
|
194
|
+
} catch {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 验证值是否为 JSON 字符串
|
|
203
|
+
*/
|
|
204
|
+
export function IsJSON(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
205
|
+
return {
|
|
206
|
+
name: 'isJSON',
|
|
207
|
+
message: options.message ?? '必须是有效的 JSON 字符串',
|
|
208
|
+
validate: (value) => {
|
|
209
|
+
if (typeof value !== 'string') {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
JSON.parse(value);
|
|
214
|
+
return true;
|
|
215
|
+
} catch {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 验证值是否相等
|
|
224
|
+
*/
|
|
225
|
+
export function Equals(comparison: unknown, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
226
|
+
return {
|
|
227
|
+
name: 'equals',
|
|
228
|
+
message: options.message ?? `必须等于 ${comparison}`,
|
|
229
|
+
validate: (value) => value === comparison,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* 验证值是否不相等
|
|
235
|
+
*/
|
|
236
|
+
export function NotEquals(comparison: unknown, options: RuleOptions = {}): ValidationRuleDefinition {
|
|
237
|
+
return {
|
|
238
|
+
name: 'notEquals',
|
|
239
|
+
message: options.message ?? `不能等于 ${comparison}`,
|
|
240
|
+
validate: (value) => value !== comparison,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 验证是否为 null 或 undefined
|
|
246
|
+
*/
|
|
247
|
+
export function IsDefined(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
248
|
+
return {
|
|
249
|
+
name: 'isDefined',
|
|
250
|
+
message: options.message ?? '必须定义',
|
|
251
|
+
validate: (value) => value !== null && value !== undefined,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 验证字符串是否只包含字母数字字符
|
|
257
|
+
*/
|
|
258
|
+
export function IsAlphanumeric(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
259
|
+
return {
|
|
260
|
+
name: 'isAlphanumeric',
|
|
261
|
+
message: options.message ?? '只能包含字母和数字',
|
|
262
|
+
validate: (value) => typeof value === 'string' && /^[a-zA-Z0-9]+$/.test(value),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 验证字符串是否只包含字母
|
|
268
|
+
*/
|
|
269
|
+
export function IsAlpha(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
270
|
+
return {
|
|
271
|
+
name: 'isAlpha',
|
|
272
|
+
message: options.message ?? '只能包含字母',
|
|
273
|
+
validate: (value) => typeof value === 'string' && /^[a-zA-Z]+$/.test(value),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* 验证字符串是否只包含数字
|
|
279
|
+
*/
|
|
280
|
+
export function IsNumberString(options: RuleOptions = {}): ValidationRuleDefinition {
|
|
281
|
+
return {
|
|
282
|
+
name: 'isNumberString',
|
|
283
|
+
message: options.message ?? '只能包含数字',
|
|
284
|
+
validate: (value) => typeof value === 'string' && /^[0-9]+$/.test(value),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ValidationRuleDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface ConditionalRuleOptions {
|
|
4
|
+
message?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 条件验证装饰器
|
|
9
|
+
* 只有当条件函数返回 true 时才执行后续验证
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* class UpdateUserDto {
|
|
13
|
+
* @Validate(ValidateIf((value, obj) => obj.type === 'premium'), IsEmail())
|
|
14
|
+
* email?: string;
|
|
15
|
+
* }
|
|
16
|
+
*/
|
|
17
|
+
export function ValidateIf(
|
|
18
|
+
condition: (value: unknown, obj?: unknown) => boolean,
|
|
19
|
+
options: ConditionalRuleOptions = {},
|
|
20
|
+
): ValidationRuleDefinition {
|
|
21
|
+
return {
|
|
22
|
+
name: 'validateIf',
|
|
23
|
+
message: options.message ?? '',
|
|
24
|
+
condition,
|
|
25
|
+
validate: () => true, // 条件验证本身总是通过,由 condition 控制是否执行后续规则
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 转换装饰器
|
|
31
|
+
* 在验证前转换值
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* class CreateUserDto {
|
|
35
|
+
* @Validate(Transform((value) => value?.trim()), IsString())
|
|
36
|
+
* name: string;
|
|
37
|
+
*
|
|
38
|
+
* @Validate(Transform((value) => Number(value)), IsInt(), Min(0))
|
|
39
|
+
* age: number;
|
|
40
|
+
* }
|
|
41
|
+
*/
|
|
42
|
+
export function Transform(
|
|
43
|
+
transformFn: (value: unknown) => unknown,
|
|
44
|
+
options: ConditionalRuleOptions = {},
|
|
45
|
+
): ValidationRuleDefinition {
|
|
46
|
+
return {
|
|
47
|
+
name: 'transform',
|
|
48
|
+
message: options.message ?? '',
|
|
49
|
+
transform: transformFn,
|
|
50
|
+
validate: () => true, // Transform 本身不验证,只转换
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export {
|
|
2
|
+
IsObject,
|
|
3
|
+
IsNotEmpty,
|
|
4
|
+
IsNotEmptyObject,
|
|
5
|
+
ValidateNested,
|
|
6
|
+
type ObjectRuleOptions,
|
|
7
|
+
type ValidateNestedOptions,
|
|
8
|
+
} from './object';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
IsArray,
|
|
12
|
+
ArrayMinSize,
|
|
13
|
+
ArrayMaxSize,
|
|
14
|
+
ArrayUnique,
|
|
15
|
+
ArrayContains,
|
|
16
|
+
ArrayNotContains,
|
|
17
|
+
ArrayNotEmpty,
|
|
18
|
+
type ArrayRuleOptions,
|
|
19
|
+
} from './array';
|
|
20
|
+
|
|
21
|
+
export {
|
|
22
|
+
IsBoolean,
|
|
23
|
+
IsInt,
|
|
24
|
+
IsPositive,
|
|
25
|
+
IsNegative,
|
|
26
|
+
Min,
|
|
27
|
+
Max,
|
|
28
|
+
IsDate,
|
|
29
|
+
IsUUID,
|
|
30
|
+
Length,
|
|
31
|
+
MaxLength,
|
|
32
|
+
Matches,
|
|
33
|
+
IsIn,
|
|
34
|
+
IsNotIn,
|
|
35
|
+
IsUrl,
|
|
36
|
+
IsJSON,
|
|
37
|
+
Equals,
|
|
38
|
+
NotEquals,
|
|
39
|
+
IsDefined,
|
|
40
|
+
IsAlphanumeric,
|
|
41
|
+
IsAlpha,
|
|
42
|
+
IsNumberString,
|
|
43
|
+
type RuleOptions,
|
|
44
|
+
type UUIDVersion,
|
|
45
|
+
} from './common';
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
ValidateIf,
|
|
49
|
+
Transform,
|
|
50
|
+
type ConditionalRuleOptions,
|
|
51
|
+
} from './conditional';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ValidationRuleDefinition } from '../types';
|
|
2
|
+
|
|
3
|
+
export interface ObjectRuleOptions {
|
|
4
|
+
message?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 验证值是否为对象
|
|
9
|
+
*/
|
|
10
|
+
export function IsObject(options: ObjectRuleOptions = {}): ValidationRuleDefinition {
|
|
11
|
+
return {
|
|
12
|
+
name: 'isObject',
|
|
13
|
+
message: options.message ?? '必须是对象',
|
|
14
|
+
validate: (value) => typeof value === 'object' && value !== null && !Array.isArray(value),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 验证值是否为非空(不是 null、undefined、空字符串、空数组、空对象)
|
|
20
|
+
*/
|
|
21
|
+
export function IsNotEmpty(options: ObjectRuleOptions = {}): ValidationRuleDefinition {
|
|
22
|
+
return {
|
|
23
|
+
name: 'isNotEmpty',
|
|
24
|
+
message: options.message ?? '不能为空',
|
|
25
|
+
validate: (value) => {
|
|
26
|
+
if (value === null || value === undefined) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === 'string') {
|
|
30
|
+
return value.trim().length > 0;
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(value)) {
|
|
33
|
+
return value.length > 0;
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === 'object') {
|
|
36
|
+
return Object.keys(value).length > 0;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 验证值是否为非空对象(对象且至少有一个属性)
|
|
45
|
+
*/
|
|
46
|
+
export function IsNotEmptyObject(options: ObjectRuleOptions = {}): ValidationRuleDefinition {
|
|
47
|
+
return {
|
|
48
|
+
name: 'isNotEmptyObject',
|
|
49
|
+
message: options.message ?? '必须是非空对象',
|
|
50
|
+
validate: (value) => {
|
|
51
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return Object.keys(value).length > 0;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ValidateNestedOptions {
|
|
60
|
+
message?: string;
|
|
61
|
+
/**
|
|
62
|
+
* 是否对数组每个元素执行验证
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
each?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 嵌套对象验证装饰器
|
|
70
|
+
* 用于标记属性需要嵌套验证
|
|
71
|
+
*/
|
|
72
|
+
export function ValidateNested(options: ValidateNestedOptions = {}): ValidationRuleDefinition {
|
|
73
|
+
return {
|
|
74
|
+
name: 'validateNested',
|
|
75
|
+
message: options.message ?? '嵌套对象验证失败',
|
|
76
|
+
nested: true,
|
|
77
|
+
each: options.each ?? false,
|
|
78
|
+
validate: (value) => {
|
|
79
|
+
// 基础检查:必须是对象或数组
|
|
80
|
+
if (options.each) {
|
|
81
|
+
return Array.isArray(value);
|
|
82
|
+
}
|
|
83
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|