@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.
Files changed (116) hide show
  1. package/README.md +129 -21
  2. package/dist/di/decorators.d.ts +37 -0
  3. package/dist/di/decorators.d.ts.map +1 -1
  4. package/dist/di/index.d.ts +1 -1
  5. package/dist/di/index.d.ts.map +1 -1
  6. package/dist/di/module-registry.d.ts +17 -0
  7. package/dist/di/module-registry.d.ts.map +1 -1
  8. package/dist/events/decorators.d.ts +52 -0
  9. package/dist/events/decorators.d.ts.map +1 -0
  10. package/dist/events/event-module.d.ts +97 -0
  11. package/dist/events/event-module.d.ts.map +1 -0
  12. package/dist/events/index.d.ts +5 -0
  13. package/dist/events/index.d.ts.map +1 -0
  14. package/dist/events/service.d.ts +76 -0
  15. package/dist/events/service.d.ts.map +1 -0
  16. package/dist/events/types.d.ts +184 -0
  17. package/dist/events/types.d.ts.map +1 -0
  18. package/dist/index.d.ts +5 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1511 -11
  21. package/dist/security/filter.d.ts +23 -0
  22. package/dist/security/filter.d.ts.map +1 -1
  23. package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
  24. package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
  25. package/dist/security/guards/builtin/index.d.ts +3 -0
  26. package/dist/security/guards/builtin/index.d.ts.map +1 -0
  27. package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
  28. package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
  29. package/dist/security/guards/decorators.d.ts +50 -0
  30. package/dist/security/guards/decorators.d.ts.map +1 -0
  31. package/dist/security/guards/execution-context.d.ts +56 -0
  32. package/dist/security/guards/execution-context.d.ts.map +1 -0
  33. package/dist/security/guards/guard-registry.d.ts +67 -0
  34. package/dist/security/guards/guard-registry.d.ts.map +1 -0
  35. package/dist/security/guards/index.d.ts +7 -0
  36. package/dist/security/guards/index.d.ts.map +1 -0
  37. package/dist/security/guards/reflector.d.ts +57 -0
  38. package/dist/security/guards/reflector.d.ts.map +1 -0
  39. package/dist/security/guards/types.d.ts +126 -0
  40. package/dist/security/guards/types.d.ts.map +1 -0
  41. package/dist/security/index.d.ts +1 -0
  42. package/dist/security/index.d.ts.map +1 -1
  43. package/dist/security/security-module.d.ts +20 -0
  44. package/dist/security/security-module.d.ts.map +1 -1
  45. package/dist/validation/class-validator.d.ts +108 -0
  46. package/dist/validation/class-validator.d.ts.map +1 -0
  47. package/dist/validation/custom-validator.d.ts +130 -0
  48. package/dist/validation/custom-validator.d.ts.map +1 -0
  49. package/dist/validation/errors.d.ts +22 -2
  50. package/dist/validation/errors.d.ts.map +1 -1
  51. package/dist/validation/index.d.ts +7 -1
  52. package/dist/validation/index.d.ts.map +1 -1
  53. package/dist/validation/rules/array.d.ts +33 -0
  54. package/dist/validation/rules/array.d.ts.map +1 -0
  55. package/dist/validation/rules/common.d.ts +90 -0
  56. package/dist/validation/rules/common.d.ts.map +1 -0
  57. package/dist/validation/rules/conditional.d.ts +30 -0
  58. package/dist/validation/rules/conditional.d.ts.map +1 -0
  59. package/dist/validation/rules/index.d.ts +5 -0
  60. package/dist/validation/rules/index.d.ts.map +1 -0
  61. package/dist/validation/rules/object.d.ts +30 -0
  62. package/dist/validation/rules/object.d.ts.map +1 -0
  63. package/dist/validation/types.d.ts +52 -1
  64. package/dist/validation/types.d.ts.map +1 -1
  65. package/docs/events.md +494 -0
  66. package/docs/guards.md +376 -0
  67. package/docs/guide.md +309 -1
  68. package/docs/request-lifecycle.md +444 -0
  69. package/docs/validation.md +407 -0
  70. package/docs/zh/events.md +494 -0
  71. package/docs/zh/guards.md +376 -0
  72. package/docs/zh/guide.md +309 -1
  73. package/docs/zh/request-lifecycle.md +444 -0
  74. package/docs/zh/validation.md +407 -0
  75. package/package.json +1 -1
  76. package/src/di/decorators.ts +46 -0
  77. package/src/di/index.ts +10 -1
  78. package/src/di/module-registry.ts +39 -0
  79. package/src/events/decorators.ts +103 -0
  80. package/src/events/event-module.ts +272 -0
  81. package/src/events/index.ts +32 -0
  82. package/src/events/service.ts +352 -0
  83. package/src/events/types.ts +223 -0
  84. package/src/index.ts +133 -1
  85. package/src/security/filter.ts +88 -8
  86. package/src/security/guards/builtin/auth-guard.ts +68 -0
  87. package/src/security/guards/builtin/index.ts +3 -0
  88. package/src/security/guards/builtin/roles-guard.ts +165 -0
  89. package/src/security/guards/decorators.ts +124 -0
  90. package/src/security/guards/execution-context.ts +152 -0
  91. package/src/security/guards/guard-registry.ts +164 -0
  92. package/src/security/guards/index.ts +7 -0
  93. package/src/security/guards/reflector.ts +99 -0
  94. package/src/security/guards/types.ts +144 -0
  95. package/src/security/index.ts +1 -0
  96. package/src/security/security-module.ts +72 -2
  97. package/src/validation/class-validator.ts +322 -0
  98. package/src/validation/custom-validator.ts +289 -0
  99. package/src/validation/errors.ts +50 -2
  100. package/src/validation/index.ts +103 -1
  101. package/src/validation/rules/array.ts +118 -0
  102. package/src/validation/rules/common.ts +286 -0
  103. package/src/validation/rules/conditional.ts +52 -0
  104. package/src/validation/rules/index.ts +51 -0
  105. package/src/validation/rules/object.ts +86 -0
  106. package/src/validation/types.ts +61 -1
  107. package/tests/di/global-module.test.ts +487 -0
  108. package/tests/events/event-decorators.test.ts +173 -0
  109. package/tests/events/event-emitter.test.ts +373 -0
  110. package/tests/events/event-module.test.ts +373 -0
  111. package/tests/security/guards/guards-integration.test.ts +371 -0
  112. package/tests/security/guards/guards.test.ts +775 -0
  113. package/tests/security/security-module.test.ts +2 -2
  114. package/tests/validation/class-validator.test.ts +349 -0
  115. package/tests/validation/custom-validator.test.ts +335 -0
  116. package/tests/validation/rules.test.ts +543 -0
@@ -0,0 +1,289 @@
1
+ import type { ValidationRuleDefinition } from './types';
2
+
3
+ export interface CustomValidatorOptions {
4
+ message?: string;
5
+ }
6
+
7
+ /**
8
+ * 创建自定义验证器
9
+ *
10
+ * @param name - 验证器名称
11
+ * @param validate - 验证函数,接收值和可选参数,返回布尔值
12
+ * @param defaultMessage - 默认错误消息
13
+ * @returns 验证规则工厂函数
14
+ *
15
+ * @example
16
+ * // 创建手机号验证器
17
+ * const IsPhoneNumber = createCustomValidator(
18
+ * 'isPhoneNumber',
19
+ * (value) => typeof value === 'string' && /^1[3-9]\d{9}$/.test(value),
20
+ * '必须是有效的手机号码'
21
+ * );
22
+ *
23
+ * // 使用
24
+ * class UserDto {
25
+ * @Validate(IsPhoneNumber())
26
+ * phone: string;
27
+ * }
28
+ *
29
+ * @example
30
+ * // 创建带参数的验证器
31
+ * const IsDivisibleBy = createCustomValidator(
32
+ * 'isDivisibleBy',
33
+ * (value, divisor: number) => typeof value === 'number' && value % divisor === 0,
34
+ * (divisor: number) => `必须能被 ${divisor} 整除`
35
+ * );
36
+ *
37
+ * // 使用
38
+ * class NumberDto {
39
+ * @Validate(IsDivisibleBy(5))
40
+ * value: number;
41
+ * }
42
+ */
43
+ export function createCustomValidator<TArgs extends unknown[] = []>(
44
+ name: string,
45
+ validate: (value: unknown, ...args: TArgs) => boolean,
46
+ defaultMessage: string | ((...args: TArgs) => string),
47
+ ): (...args: TArgs) => (options?: CustomValidatorOptions) => ValidationRuleDefinition {
48
+ return (...args: TArgs) =>
49
+ (options: CustomValidatorOptions = {}): ValidationRuleDefinition => {
50
+ const message =
51
+ options.message ??
52
+ (typeof defaultMessage === 'function' ? defaultMessage(...args) : defaultMessage);
53
+
54
+ return {
55
+ name,
56
+ message,
57
+ validate: (value: unknown) => validate(value, ...args),
58
+ };
59
+ };
60
+ }
61
+
62
+ /**
63
+ * 创建简单自定义验证器(无参数)
64
+ *
65
+ * @param name - 验证器名称
66
+ * @param validate - 验证函数
67
+ * @param defaultMessage - 默认错误消息
68
+ * @returns 验证规则工厂函数
69
+ *
70
+ * @example
71
+ * const IsPhoneNumber = createSimpleValidator(
72
+ * 'isPhoneNumber',
73
+ * (value) => typeof value === 'string' && /^1[3-9]\d{9}$/.test(value),
74
+ * '必须是有效的手机号码'
75
+ * );
76
+ *
77
+ * // 使用
78
+ * @Validate(IsPhoneNumber())
79
+ * phone: string;
80
+ */
81
+ export function createSimpleValidator(
82
+ name: string,
83
+ validate: (value: unknown) => boolean,
84
+ defaultMessage: string,
85
+ ): (options?: CustomValidatorOptions) => ValidationRuleDefinition {
86
+ return (options: CustomValidatorOptions = {}): ValidationRuleDefinition => ({
87
+ name,
88
+ message: options.message ?? defaultMessage,
89
+ validate,
90
+ });
91
+ }
92
+
93
+ /**
94
+ * 创建正则表达式验证器
95
+ *
96
+ * @param name - 验证器名称
97
+ * @param pattern - 正则表达式
98
+ * @param defaultMessage - 默认错误消息
99
+ * @returns 验证规则工厂函数
100
+ *
101
+ * @example
102
+ * const IsSlug = createRegexValidator(
103
+ * 'isSlug',
104
+ * /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
105
+ * '必须是有效的 slug 格式'
106
+ * );
107
+ */
108
+ export function createRegexValidator(
109
+ name: string,
110
+ pattern: RegExp,
111
+ defaultMessage: string,
112
+ ): (options?: CustomValidatorOptions) => ValidationRuleDefinition {
113
+ return createSimpleValidator(
114
+ name,
115
+ (value) => typeof value === 'string' && pattern.test(value),
116
+ defaultMessage,
117
+ );
118
+ }
119
+
120
+ // ============= 内置扩展验证器 =============
121
+
122
+ /**
123
+ * 验证中国大陆手机号
124
+ */
125
+ export const IsPhoneNumber = createSimpleValidator(
126
+ 'isPhoneNumber',
127
+ (value) => typeof value === 'string' && /^1[3-9]\d{9}$/.test(value),
128
+ '必须是有效的手机号码',
129
+ );
130
+
131
+ /**
132
+ * 验证中国身份证号
133
+ */
134
+ export const IsIdCard = createSimpleValidator(
135
+ 'isIdCard',
136
+ (value) => {
137
+ if (typeof value !== 'string') return false;
138
+ // 15位身份证:6位地区码 + 6位出生日期(YYMMDD) + 3位顺序码
139
+ const pattern15 = /^[1-9]\d{5}\d{2}((0[1-9])|(1[0-2]))(([0-2]\d)|30|31)\d{3}$/;
140
+ // 18位身份证:6位地区码 + 8位出生日期(YYYYMMDD) + 3位顺序码 + 1位校验码
141
+ const pattern18 = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2]\d)|30|31)\d{3}[0-9Xx]$/;
142
+ return pattern15.test(value) || pattern18.test(value);
143
+ },
144
+ '必须是有效的身份证号码',
145
+ );
146
+
147
+ /**
148
+ * 验证 IP 地址 (IPv4)
149
+ */
150
+ export const IsIPv4 = createSimpleValidator(
151
+ 'isIPv4',
152
+ (value) => {
153
+ if (typeof value !== 'string') return false;
154
+ const parts = value.split('.');
155
+ if (parts.length !== 4) return false;
156
+ return parts.every((part) => {
157
+ const num = parseInt(part, 10);
158
+ return !Number.isNaN(num) && num >= 0 && num <= 255 && String(num) === part;
159
+ });
160
+ },
161
+ '必须是有效的 IPv4 地址',
162
+ );
163
+
164
+ /**
165
+ * 验证端口号
166
+ */
167
+ export const IsPort = createSimpleValidator(
168
+ 'isPort',
169
+ (value) => {
170
+ if (typeof value === 'string') {
171
+ const num = parseInt(value, 10);
172
+ return !Number.isNaN(num) && num >= 0 && num <= 65535;
173
+ }
174
+ if (typeof value === 'number') {
175
+ return Number.isInteger(value) && value >= 0 && value <= 65535;
176
+ }
177
+ return false;
178
+ },
179
+ '必须是有效的端口号 (0-65535)',
180
+ );
181
+
182
+ /**
183
+ * 验证邮政编码(中国)
184
+ */
185
+ export const IsPostalCode = createSimpleValidator(
186
+ 'isPostalCode',
187
+ (value) => typeof value === 'string' && /^[1-9]\d{5}$/.test(value),
188
+ '必须是有效的邮政编码',
189
+ );
190
+
191
+ /**
192
+ * 验证信用卡号(Luhn 算法)
193
+ */
194
+ export const IsCreditCard = createSimpleValidator(
195
+ 'isCreditCard',
196
+ (value) => {
197
+ if (typeof value !== 'string') return false;
198
+ const sanitized = value.replace(/[\s-]/g, '');
199
+ if (!/^\d{13,19}$/.test(sanitized)) return false;
200
+
201
+ // Luhn 算法
202
+ let sum = 0;
203
+ let isEven = false;
204
+ for (let i = sanitized.length - 1; i >= 0; i--) {
205
+ let digit = parseInt(sanitized[i], 10);
206
+ if (isEven) {
207
+ digit *= 2;
208
+ if (digit > 9) digit -= 9;
209
+ }
210
+ sum += digit;
211
+ isEven = !isEven;
212
+ }
213
+ return sum % 10 === 0;
214
+ },
215
+ '必须是有效的信用卡号',
216
+ );
217
+
218
+ /**
219
+ * 验证十六进制颜色值
220
+ */
221
+ export const IsHexColor = createSimpleValidator(
222
+ 'isHexColor',
223
+ (value) => typeof value === 'string' && /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(value),
224
+ '必须是有效的十六进制颜色值',
225
+ );
226
+
227
+ /**
228
+ * 验证 MAC 地址
229
+ */
230
+ export const IsMacAddress = createSimpleValidator(
231
+ 'isMacAddress',
232
+ (value) =>
233
+ typeof value === 'string' &&
234
+ /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/.test(value),
235
+ '必须是有效的 MAC 地址',
236
+ );
237
+
238
+ /**
239
+ * 验证语义化版本号
240
+ */
241
+ export const IsSemVer = createSimpleValidator(
242
+ 'isSemVer',
243
+ (value) =>
244
+ typeof value === 'string' &&
245
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(
246
+ value,
247
+ ),
248
+ '必须是有效的语义化版本号',
249
+ );
250
+
251
+ /**
252
+ * 创建能被指定数字整除的验证器
253
+ */
254
+ export const IsDivisibleBy = createCustomValidator(
255
+ 'isDivisibleBy',
256
+ (value: unknown, divisor: number) =>
257
+ typeof value === 'number' && !Number.isNaN(value) && value % divisor === 0,
258
+ (divisor: number) => `必须能被 ${divisor} 整除`,
259
+ );
260
+
261
+ /**
262
+ * 创建范围验证器
263
+ */
264
+ export const IsBetween = createCustomValidator(
265
+ 'isBetween',
266
+ (value: unknown, min: number, max: number) =>
267
+ typeof value === 'number' && value >= min && value <= max,
268
+ (min: number, max: number) => `必须在 ${min} 和 ${max} 之间`,
269
+ );
270
+
271
+ /**
272
+ * 验证是否包含指定子字符串
273
+ */
274
+ export const Contains = createCustomValidator(
275
+ 'contains',
276
+ (value: unknown, substring: string) =>
277
+ typeof value === 'string' && value.includes(substring),
278
+ (substring: string) => `必须包含 "${substring}"`,
279
+ );
280
+
281
+ /**
282
+ * 验证是否不包含指定子字符串
283
+ */
284
+ export const NotContains = createCustomValidator(
285
+ 'notContains',
286
+ (value: unknown, substring: string) =>
287
+ typeof value === 'string' && !value.includes(substring),
288
+ (substring: string) => `不能包含 "${substring}"`,
289
+ );
@@ -1,8 +1,13 @@
1
1
  export interface ValidationIssue {
2
2
  /**
3
- * 参数索引
3
+ * 参数索引(参数级别验证使用)
4
4
  */
5
- index: number;
5
+ index?: number;
6
+
7
+ /**
8
+ * 属性路径(类级别验证使用,支持嵌套如 'user.address.city')
9
+ */
10
+ property?: string;
6
11
 
7
12
  /**
8
13
  * 失败的规则名称
@@ -13,6 +18,16 @@ export interface ValidationIssue {
13
18
  * 错误信息
14
19
  */
15
20
  message: string;
21
+
22
+ /**
23
+ * 验证失败的值
24
+ */
25
+ value?: unknown;
26
+
27
+ /**
28
+ * 嵌套错误(用于嵌套对象验证)
29
+ */
30
+ children?: ValidationIssue[];
16
31
  }
17
32
 
18
33
  export class ValidationError extends Error {
@@ -23,6 +38,39 @@ export class ValidationError extends Error {
23
38
  this.name = 'ValidationError';
24
39
  this.issues = issues;
25
40
  }
41
+
42
+ /**
43
+ * 获取所有错误的扁平化列表
44
+ */
45
+ public getFlattened(): ValidationIssue[] {
46
+ const flatten = (issues: ValidationIssue[], prefix = ''): ValidationIssue[] => {
47
+ const result: ValidationIssue[] = [];
48
+ for (const issue of issues) {
49
+ const propertyPath = prefix ? `${prefix}.${issue.property ?? ''}` : (issue.property ?? '');
50
+ if (issue.children && issue.children.length > 0) {
51
+ result.push(...flatten(issue.children, propertyPath));
52
+ } else {
53
+ result.push({
54
+ ...issue,
55
+ property: propertyPath || undefined,
56
+ });
57
+ }
58
+ }
59
+ return result;
60
+ };
61
+ return flatten(this.issues);
62
+ }
63
+
64
+ /**
65
+ * 转换为简单的错误对象
66
+ */
67
+ public toJSON(): Record<string, unknown> {
68
+ return {
69
+ name: this.name,
70
+ message: this.message,
71
+ issues: this.issues,
72
+ };
73
+ }
26
74
  }
27
75
 
28
76
 
@@ -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
- export type { ValidationRuleDefinition, ValidationMetadata } from './types';
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
+ }