@hestjs/validation 0.1.2

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.
@@ -0,0 +1,186 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { Injectable } from "@hestjs/core";
11
+ import { Type } from "@sinclair/typebox";
12
+ import { Value } from "@sinclair/typebox/value";
13
+ import { VALIDATION_METADATA_KEY, } from "../decorators/validation";
14
+ /**
15
+ * 验证错误
16
+ */
17
+ export class ValidationError extends Error {
18
+ field;
19
+ value;
20
+ constraint;
21
+ constructor(message, field, value, constraint) {
22
+ super(message);
23
+ this.field = field;
24
+ this.value = value;
25
+ this.constraint = constraint;
26
+ this.name = "ValidationError";
27
+ }
28
+ }
29
+ /**
30
+ * 验证异常 - 包含多个验证错误
31
+ */
32
+ export class ValidationException extends Error {
33
+ errors;
34
+ constructor(errors) {
35
+ super("Validation failed");
36
+ this.errors = errors;
37
+ this.name = "ValidationException";
38
+ }
39
+ getMessages() {
40
+ return this.errors.map((error) => error.message);
41
+ }
42
+ getFormattedMessage() {
43
+ return this.errors
44
+ .map((error) => `${error.field}: ${error.message}`)
45
+ .join("; ");
46
+ }
47
+ }
48
+ /**
49
+ * 验证管道
50
+ */
51
+ let ValidationPipe = class ValidationPipe {
52
+ options;
53
+ constructor(options = {}) {
54
+ this.options = {
55
+ whitelist: true,
56
+ forbidNonWhitelisted: false,
57
+ transform: true,
58
+ disableErrorMessages: false,
59
+ validateCustomDecorators: true,
60
+ ...options,
61
+ };
62
+ }
63
+ /**
64
+ * 验证对象
65
+ */
66
+ async validate(target, object) {
67
+ if (!object || typeof object !== "object") {
68
+ throw new ValidationException([
69
+ new ValidationError("Invalid input", "root", object, "object"),
70
+ ]);
71
+ }
72
+ const metadata = Reflect.getMetadata(VALIDATION_METADATA_KEY, target);
73
+ if (!metadata || !metadata.properties.length) {
74
+ // 如果没有验证元数据,直接返回对象
75
+ return object;
76
+ }
77
+ const errors = [];
78
+ const result = {};
79
+ // 创建 TypeBox 对象 schema
80
+ const schemaProperties = {};
81
+ const requiredFields = [];
82
+ for (const prop of metadata.properties) {
83
+ const propName = String(prop.propertyKey);
84
+ schemaProperties[propName] = prop.schema;
85
+ if (!prop.isOptional) {
86
+ requiredFields.push(propName);
87
+ }
88
+ }
89
+ const objectSchema = Type.Object(schemaProperties, {
90
+ required: requiredFields,
91
+ additionalProperties: !this.options.whitelist,
92
+ });
93
+ // 使用 TypeBox 进行验证
94
+ const isValid = Value.Check(objectSchema, object);
95
+ if (!isValid) {
96
+ // 获取详细错误
97
+ const validationErrors = [...Value.Errors(objectSchema, object)];
98
+ for (const error of validationErrors) {
99
+ const field = error.path.replace(/^\//, "") || "root";
100
+ const customMessage = this.getCustomMessage(metadata, field);
101
+ errors.push(new ValidationError(customMessage || error.message, field, error.value, String(error.type || "validation")));
102
+ }
103
+ }
104
+ if (errors.length > 0) {
105
+ throw new ValidationException(errors);
106
+ }
107
+ // 如果启用了转换,使用 TypeBox 进行类型转换
108
+ if (this.options.transform) {
109
+ try {
110
+ const transformed = Value.Convert(objectSchema, object);
111
+ return this.filterProperties(transformed, metadata);
112
+ }
113
+ catch (error) {
114
+ // 转换失败,使用原始对象
115
+ return this.filterProperties(object, metadata);
116
+ }
117
+ }
118
+ return this.filterProperties(object, metadata);
119
+ }
120
+ /**
121
+ * 过滤属性(白名单模式)
122
+ */
123
+ filterProperties(object, metadata) {
124
+ if (!this.options.whitelist) {
125
+ return object;
126
+ }
127
+ const result = {};
128
+ const allowedProperties = new Set(metadata.properties.map((prop) => String(prop.propertyKey)));
129
+ for (const [key, value] of Object.entries(object)) {
130
+ if (allowedProperties.has(key)) {
131
+ result[key] = value;
132
+ }
133
+ else if (this.options.forbidNonWhitelisted) {
134
+ throw new ValidationException([
135
+ new ValidationError(`Property ${key} should not exist`, key, value, "whitelistValidation"),
136
+ ]);
137
+ }
138
+ }
139
+ return result;
140
+ }
141
+ /**
142
+ * 获取自定义错误消息
143
+ */
144
+ getCustomMessage(metadata, field) {
145
+ const property = metadata.properties.find((prop) => String(prop.propertyKey) === field);
146
+ return property?.message;
147
+ }
148
+ /**
149
+ * 验证单个值
150
+ */
151
+ validateValue(schema, value, fieldName = "value") {
152
+ const isValid = Value.Check(schema, value);
153
+ if (!isValid) {
154
+ const errors = [...Value.Errors(schema, value)];
155
+ const validationErrors = errors.map((error) => new ValidationError(error.message, fieldName, error.value, String(error.type || "validation")));
156
+ throw new ValidationException(validationErrors);
157
+ }
158
+ if (this.options.transform) {
159
+ try {
160
+ return Value.Convert(schema, value);
161
+ }
162
+ catch {
163
+ return value;
164
+ }
165
+ }
166
+ return value;
167
+ }
168
+ /**
169
+ * 创建 DTO 验证装饰器
170
+ */
171
+ static createDtoValidator(dtoClass) {
172
+ return function (target, propertyKey, parameterIndex) {
173
+ // 这里可以存储参数验证元数据
174
+ // 实际的验证会在拦截器中进行
175
+ const existingMetadata = Reflect.getMetadata("validation:parameters", target) || {};
176
+ existingMetadata[parameterIndex] = dtoClass;
177
+ Reflect.defineMetadata("validation:parameters", existingMetadata, target);
178
+ };
179
+ }
180
+ };
181
+ ValidationPipe = __decorate([
182
+ Injectable(),
183
+ __metadata("design:paramtypes", [Object])
184
+ ], ValidationPipe);
185
+ export { ValidationPipe };
186
+ //# sourceMappingURL=validation.pipe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.pipe.js","sourceRoot":"","sources":["../../src/pipes/validation.pipe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import { type TSchema } from '@sinclair/typebox';
2
+ /**
3
+ * 验证工具类
4
+ */
5
+ export declare class ValidationUtils {
6
+ private static readonly defaultPipe;
7
+ /**
8
+ * 验证对象
9
+ */
10
+ static validateObject<T>(target: new () => T, object: any): Promise<T>;
11
+ /**
12
+ * 验证值
13
+ */
14
+ static validateValue<T>(schema: TSchema, value: any, fieldName?: string): T;
15
+ /**
16
+ * 检查对象是否有效
17
+ */
18
+ static isValid<T>(target: new () => T, object: any): Promise<boolean>;
19
+ /**
20
+ * 获取验证错误而不抛出异常
21
+ */
22
+ static getValidationErrors<T>(target: new () => T, object: any): Promise<string[]>;
23
+ /**
24
+ * 从类生成 TypeBox Schema
25
+ */
26
+ static generateSchema<T>(target: new () => T): TSchema;
27
+ /**
28
+ * 类型安全的转换函数
29
+ */
30
+ static transform<T>(schema: TSchema, value: any): T;
31
+ /**
32
+ * 创建自定义验证装饰器
33
+ */
34
+ static createCustomValidator(validator: (value: any) => boolean, message?: string): (target: any, propertyKey: string | symbol) => void;
35
+ /**
36
+ * 批量验证多个对象
37
+ */
38
+ static validateBatch<T>(target: new () => T, objects: any[]): Promise<{
39
+ valid: T[];
40
+ errors: Array<{
41
+ index: number;
42
+ errors: string[];
43
+ }>;
44
+ }>;
45
+ /**
46
+ * 验证部分对象(仅验证存在的字段)
47
+ */
48
+ static validatePartial<T>(target: new () => T, object: any): Promise<Partial<T>>;
49
+ }
50
+ //# sourceMappingURL=validation.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.utils.d.ts","sourceRoot":"","sources":["../../src/utils/validation.utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,OAAO,EAAe,MAAM,mBAAmB,CAAC;AAKpE;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAwB;IAE3D;;OAEG;WACU,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC;IAI5E;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC;IAI3E;;OAEG;WACU,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC;IAS3E;;OAEG;WACU,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAYxF;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,GAAG,OAAO;IAyBtD;;OAEG;IACH,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;IAanD;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAC1B,SAAS,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,OAAO,EAClC,OAAO,GAAE,MAAmC,IAE3B,QAAQ,GAAG,EAAE,aAAa,MAAM,GAAG,MAAM;IAkB5D;;OAEG;WACU,aAAa,CAAC,CAAC,EAC1B,MAAM,EAAE,UAAU,CAAC,EACnB,OAAO,EAAE,GAAG,EAAE,GACb,OAAO,CAAC;QAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAAC,MAAM,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IAuB9E;;OAEG;WACU,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CAqCvF"}
@@ -0,0 +1,162 @@
1
+ import { Type } from '@sinclair/typebox';
2
+ import { Value } from '@sinclair/typebox/value';
3
+ import { ValidationPipe, ValidationException } from '../pipes/validation.pipe';
4
+ import { VALIDATION_METADATA_KEY } from '../decorators/validation';
5
+ /**
6
+ * 验证工具类
7
+ */
8
+ export class ValidationUtils {
9
+ static defaultPipe = new ValidationPipe();
10
+ /**
11
+ * 验证对象
12
+ */
13
+ static async validateObject(target, object) {
14
+ return this.defaultPipe.validate(target, object);
15
+ }
16
+ /**
17
+ * 验证值
18
+ */
19
+ static validateValue(schema, value, fieldName) {
20
+ return this.defaultPipe.validateValue(schema, value, fieldName);
21
+ }
22
+ /**
23
+ * 检查对象是否有效
24
+ */
25
+ static async isValid(target, object) {
26
+ try {
27
+ await this.validateObject(target, object);
28
+ return true;
29
+ }
30
+ catch (error) {
31
+ return false;
32
+ }
33
+ }
34
+ /**
35
+ * 获取验证错误而不抛出异常
36
+ */
37
+ static async getValidationErrors(target, object) {
38
+ try {
39
+ await this.validateObject(target, object);
40
+ return [];
41
+ }
42
+ catch (error) {
43
+ if (error instanceof ValidationException) {
44
+ return error.getMessages();
45
+ }
46
+ return [error instanceof Error ? error.message : 'Unknown validation error'];
47
+ }
48
+ }
49
+ /**
50
+ * 从类生成 TypeBox Schema
51
+ */
52
+ static generateSchema(target) {
53
+ const metadata = Reflect.getMetadata(VALIDATION_METADATA_KEY, target);
54
+ if (!metadata || !metadata.properties.length) {
55
+ return Type.Object({});
56
+ }
57
+ const properties = {};
58
+ const requiredFields = [];
59
+ for (const prop of metadata.properties) {
60
+ const propName = String(prop.propertyKey);
61
+ properties[propName] = prop.schema;
62
+ if (!prop.isOptional) {
63
+ requiredFields.push(propName);
64
+ }
65
+ }
66
+ return Type.Object(properties, {
67
+ required: requiredFields,
68
+ additionalProperties: false
69
+ });
70
+ }
71
+ /**
72
+ * 类型安全的转换函数
73
+ */
74
+ static transform(schema, value) {
75
+ try {
76
+ return Value.Convert(schema, value);
77
+ }
78
+ catch (error) {
79
+ throw new ValidationException([{
80
+ message: `Transform failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
81
+ field: 'root',
82
+ value,
83
+ constraint: 'transform'
84
+ }]);
85
+ }
86
+ }
87
+ /**
88
+ * 创建自定义验证装饰器
89
+ */
90
+ static createCustomValidator(validator, message = 'Custom validation failed') {
91
+ return function (target, propertyKey) {
92
+ // 这里可以实现自定义验证逻辑
93
+ // 为了简化,我们使用一个通用的字符串验证
94
+ const schema = Type.String();
95
+ const existingMetadata = Reflect.getMetadata(VALIDATION_METADATA_KEY, target.constructor) || { properties: [] };
96
+ existingMetadata.properties.push({
97
+ propertyKey,
98
+ schema,
99
+ message,
100
+ });
101
+ Reflect.defineMetadata(VALIDATION_METADATA_KEY, existingMetadata, target.constructor);
102
+ };
103
+ }
104
+ /**
105
+ * 批量验证多个对象
106
+ */
107
+ static async validateBatch(target, objects) {
108
+ const valid = [];
109
+ const errors = [];
110
+ for (let i = 0; i < objects.length; i++) {
111
+ try {
112
+ const validated = await this.validateObject(target, objects[i]);
113
+ valid.push(validated);
114
+ }
115
+ catch (error) {
116
+ if (error instanceof ValidationException) {
117
+ errors.push({ index: i, errors: error.getMessages() });
118
+ }
119
+ else {
120
+ errors.push({
121
+ index: i,
122
+ errors: [error instanceof Error ? error.message : 'Unknown error']
123
+ });
124
+ }
125
+ }
126
+ }
127
+ return { valid, errors };
128
+ }
129
+ /**
130
+ * 验证部分对象(仅验证存在的字段)
131
+ */
132
+ static async validatePartial(target, object) {
133
+ const pipe = new ValidationPipe({
134
+ whitelist: true,
135
+ forbidNonWhitelisted: false,
136
+ transform: true,
137
+ });
138
+ // 创建一个只包含存在字段的新类
139
+ const metadata = Reflect.getMetadata(VALIDATION_METADATA_KEY, target);
140
+ if (!metadata || !metadata.properties.length) {
141
+ return object;
142
+ }
143
+ // 过滤出存在的属性
144
+ const existingProps = metadata.properties.filter(prop => object.hasOwnProperty(String(prop.propertyKey)));
145
+ if (existingProps.length === 0) {
146
+ return {};
147
+ }
148
+ // 创建临时验证元数据
149
+ const tempMetadata = {
150
+ properties: existingProps.map(prop => ({
151
+ ...prop,
152
+ isOptional: true, // 所有字段都标记为可选
153
+ }))
154
+ };
155
+ // 创建临时类
156
+ class TempClass {
157
+ }
158
+ Reflect.defineMetadata(VALIDATION_METADATA_KEY, tempMetadata, TempClass);
159
+ return pipe.validate(TempClass, object);
160
+ }
161
+ }
162
+ //# sourceMappingURL=validation.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.utils.js","sourceRoot":"","sources":["../../src/utils/validation.utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@hestjs/validation",
3
+ "version": "0.1.2",
4
+ "description": "HestJS Validation Module with TypeBox",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "clean": "rm -rf dist",
14
+ "check-types": "tsc --noEmit"
15
+ },
16
+ "dependencies": {
17
+ "@hestjs/core": "workspace:*",
18
+ "@sinclair/typebox": "^0.32.15",
19
+ "reflect-metadata": "^0.2.2"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.0.0",
23
+ "typescript": "5.8.3"
24
+ },
25
+ "peerDependencies": {
26
+ "typescript": ">=5.0.0"
27
+ },
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.js",
32
+ "types": "./dist/index.d.ts"
33
+ }
34
+ }
35
+ }