@dangao/bun-server 1.7.1 → 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 +129 -21
- package/dist/di/decorators.d.ts +37 -0
- package/dist/di/decorators.d.ts.map +1 -1
- package/dist/di/index.d.ts +1 -1
- 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/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 +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1511 -11
- 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/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/validation.md +407 -0
- package/package.json +1 -1
- package/src/di/decorators.ts +46 -0
- package/src/di/index.ts +10 -1
- package/src/di/module-registry.ts +39 -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 +133 -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/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
|
@@ -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
|
+
}
|
package/src/validation/types.ts
CHANGED
|
@@ -11,13 +11,41 @@ export interface ValidationRuleDefinition {
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* 校验函数
|
|
14
|
+
* @param value - 要验证的值
|
|
15
|
+
* @param obj - 完整对象(用于条件验证)
|
|
14
16
|
*/
|
|
15
|
-
validate: (value: unknown) => boolean;
|
|
17
|
+
validate: (value: unknown, obj?: unknown) => boolean;
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* 是否为可选字段
|
|
19
21
|
*/
|
|
20
22
|
optional?: boolean;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 条件验证函数
|
|
26
|
+
* 返回 true 时执行验证,返回 false 时跳过
|
|
27
|
+
*/
|
|
28
|
+
condition?: (value: unknown, obj?: unknown) => boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 转换函数(在验证前执行)
|
|
32
|
+
*/
|
|
33
|
+
transform?: (value: unknown) => unknown;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 是否为嵌套验证
|
|
37
|
+
*/
|
|
38
|
+
nested?: boolean;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 嵌套验证的类型
|
|
42
|
+
*/
|
|
43
|
+
nestedType?: new () => unknown;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 是否对数组每个元素执行嵌套验证
|
|
47
|
+
*/
|
|
48
|
+
each?: boolean;
|
|
21
49
|
}
|
|
22
50
|
|
|
23
51
|
export interface ValidationMetadata {
|
|
@@ -32,4 +60,36 @@ export interface ValidationMetadata {
|
|
|
32
60
|
rules: ValidationRuleDefinition[];
|
|
33
61
|
}
|
|
34
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 类级别验证元数据
|
|
65
|
+
*/
|
|
66
|
+
export interface ClassValidationMetadata {
|
|
67
|
+
/**
|
|
68
|
+
* 属性名
|
|
69
|
+
*/
|
|
70
|
+
property: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 规则列表
|
|
74
|
+
*/
|
|
75
|
+
rules: ValidationRuleDefinition[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 验证选项
|
|
80
|
+
*/
|
|
81
|
+
export interface ValidationOptions {
|
|
82
|
+
/**
|
|
83
|
+
* 是否在第一个错误时停止验证
|
|
84
|
+
* @default false
|
|
85
|
+
*/
|
|
86
|
+
stopAtFirstError?: boolean;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 是否跳过缺失的属性
|
|
90
|
+
* @default false
|
|
91
|
+
*/
|
|
92
|
+
skipMissingProperties?: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
35
95
|
|