@tsed/ajv 8.26.2 → 8.28.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/lib/esm/fn/validate.js +11 -2
- package/lib/esm/index.js +2 -0
- package/lib/esm/services/AjvService.js +37 -57
- package/lib/esm/utils/mapClassError.js +23 -0
- package/lib/esm/utils/mapErrors.js +42 -0
- package/lib/types/fn/validate.d.ts +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/services/AjvService.d.ts +18 -7
- package/lib/types/utils/mapClassError.d.ts +13 -0
- package/lib/types/utils/mapErrors.d.ts +21 -0
- package/package.json +8 -8
package/lib/esm/fn/validate.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inject } from "@tsed/di";
|
|
2
|
+
import { JsonSchema } from "@tsed/schema";
|
|
2
3
|
import { AjvService } from "../services/AjvService.js";
|
|
3
4
|
/**
|
|
4
5
|
* Validate a value against a JSON schema or a type using the configured `AjvService`.
|
|
@@ -9,10 +10,18 @@ import { AjvService } from "../services/AjvService.js";
|
|
|
9
10
|
* @typeParam TReturn Explicit return type override. When omitted, `TValue` is returned.
|
|
10
11
|
* @typeParam TValue Input value type.
|
|
11
12
|
* @param value Value to validate.
|
|
12
|
-
* @param options Validation options, a
|
|
13
|
+
* @param options Validation options, a `JsonSchema`, or a target type descriptor.
|
|
13
14
|
* @returns The validated value. If `returnsCoercedValues` is enabled, it may contain coerced values.
|
|
14
15
|
* @throws {AjvValidationError} When validation fails.
|
|
15
16
|
*/
|
|
16
17
|
export function validate(value, options) {
|
|
17
|
-
return inject(AjvService).validate(value, options);
|
|
18
|
+
return inject(AjvService).validate(value, mapOptions(options));
|
|
19
|
+
}
|
|
20
|
+
function mapOptions(options) {
|
|
21
|
+
if (options instanceof JsonSchema) {
|
|
22
|
+
return {
|
|
23
|
+
schema: options
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return options;
|
|
18
27
|
}
|
package/lib/esm/index.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import "./Ajv.js";
|
|
2
|
-
import { deepClone
|
|
2
|
+
import { deepClone } from "@tsed/core";
|
|
3
3
|
import { constant, inject, injectable } from "@tsed/di";
|
|
4
|
-
import {
|
|
4
|
+
import { compile, JsonSchema } from "@tsed/schema";
|
|
5
5
|
import { Ajv } from "ajv";
|
|
6
|
-
import { AjvValidationError } from "../errors/AjvValidationError.js";
|
|
7
6
|
import { defaultErrorFormatter } from "../utils/defaultErrorFormatter.js";
|
|
8
|
-
import {
|
|
7
|
+
import { mapErrors } from "../utils/mapErrors.js";
|
|
9
8
|
export class AjvService {
|
|
10
9
|
constructor() {
|
|
11
10
|
this.name = "ajv";
|
|
12
11
|
this.errorFormatter = constant("ajv.errorFormatter", defaultErrorFormatter);
|
|
13
12
|
this.returnsCoercedValues = constant("ajv.returnsCoercedValues");
|
|
13
|
+
this.hasLoadSchema = !!constant("ajv.loadSchema");
|
|
14
14
|
this.ajv = inject(Ajv);
|
|
15
|
+
this.validatorsBySchema = new WeakMap();
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* Validate a value against a JSON schema, a model type, or explicit AJV options.
|
|
@@ -29,19 +30,18 @@ export class AjvService {
|
|
|
29
30
|
* @throws {AjvValidationError} When validation fails.
|
|
30
31
|
*/
|
|
31
32
|
async validate(value, options) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const localValue =
|
|
36
|
-
const validate = this.ajv.compile(schema);
|
|
33
|
+
if (options.type || options.schema) {
|
|
34
|
+
let { schema: defaultSchema, type, collectionType, returnsCoercedValues = this.returnsCoercedValues, ...additionalOptions } = options;
|
|
35
|
+
const validate = await this.getValidator(defaultSchema || type, additionalOptions);
|
|
36
|
+
const localValue = returnsCoercedValues ? value : deepClone(value);
|
|
37
37
|
const valid = await validate(localValue);
|
|
38
38
|
const { errors } = validate;
|
|
39
39
|
if (!valid && errors) {
|
|
40
|
-
throw
|
|
40
|
+
throw mapErrors(errors, {
|
|
41
41
|
type,
|
|
42
42
|
collectionType,
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
value: localValue,
|
|
44
|
+
errorFormatter: this.errorFormatter
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
if (returnsCoercedValues) {
|
|
@@ -50,52 +50,32 @@ export class AjvService {
|
|
|
50
50
|
}
|
|
51
51
|
return value;
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
if (dataPath && dataPath.match(/pwd|password|mdp|secret/)) {
|
|
78
|
-
error.data = "[REDACTED]";
|
|
79
|
-
}
|
|
80
|
-
if (type) {
|
|
81
|
-
error.modelName = nameOf(type);
|
|
82
|
-
error.message = this.mapClassError(error, type);
|
|
83
|
-
}
|
|
84
|
-
return this.errorFormatter(error);
|
|
85
|
-
})
|
|
86
|
-
.join("\n");
|
|
87
|
-
return new AjvValidationError(message, errors);
|
|
88
|
-
}
|
|
89
|
-
mapClassError(error, targetType) {
|
|
90
|
-
const propertyKey = getValue(error, "params.missingProperty");
|
|
91
|
-
if (propertyKey) {
|
|
92
|
-
const store = JsonEntityStore.from(prototypeOf(targetType), propertyKey);
|
|
93
|
-
if (store) {
|
|
94
|
-
setValue(error, "params.missingProperty", store.name || propertyKey);
|
|
95
|
-
return error.message?.replace(`'${propertyKey}'`, `'${store.name || propertyKey}'`);
|
|
96
|
-
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve and compile a validator for the provided schema input.
|
|
55
|
+
*
|
|
56
|
+
* The compiled validator is cached by schema object identity to avoid
|
|
57
|
+
* recompiling validators for repeated calls using the same schema instance.
|
|
58
|
+
*
|
|
59
|
+
* When `loadSchema` is configured, this method uses `compileAsync` to support
|
|
60
|
+
* asynchronous external reference resolution.
|
|
61
|
+
*
|
|
62
|
+
* @param schema Schema source as `JsonSchema` instance or plain JSON schema object.
|
|
63
|
+
* @returns Compiled AJV validate function.
|
|
64
|
+
*/
|
|
65
|
+
async getValidator(input, additionalOptions) {
|
|
66
|
+
const schema = input instanceof JsonSchema || typeof input === "function"
|
|
67
|
+
? compile(input, {
|
|
68
|
+
...additionalOptions,
|
|
69
|
+
customKeys: true
|
|
70
|
+
})
|
|
71
|
+
: input;
|
|
72
|
+
const cached = this.validatorsBySchema.get(schema);
|
|
73
|
+
if (cached) {
|
|
74
|
+
return cached;
|
|
97
75
|
}
|
|
98
|
-
|
|
76
|
+
const validate = this.hasLoadSchema ? await this.ajv.compileAsync(schema) : this.ajv.compile(schema);
|
|
77
|
+
this.validatorsBySchema.set(schema, validate);
|
|
78
|
+
return validate;
|
|
99
79
|
}
|
|
100
80
|
}
|
|
101
81
|
injectable(AjvService).type("validator:service");
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getValue, prototypeOf, setValue } from "@tsed/core";
|
|
2
|
+
import { JsonEntityStore } from "@tsed/schema";
|
|
3
|
+
/**
|
|
4
|
+
* Rewrite class-related AJV messages using Ts.ED metadata.
|
|
5
|
+
*
|
|
6
|
+
* For missing required properties, this method replaces the property key by
|
|
7
|
+
* the store-resolved property name when aliasing/naming is configured.
|
|
8
|
+
*
|
|
9
|
+
* @param error AJV error object to transform.
|
|
10
|
+
* @param targetType Class used to resolve property metadata.
|
|
11
|
+
* @returns Updated message when metadata exists, otherwise original message.
|
|
12
|
+
*/
|
|
13
|
+
export function mapClassError(error, targetType) {
|
|
14
|
+
const propertyKey = getValue(error, "params.missingProperty");
|
|
15
|
+
if (propertyKey) {
|
|
16
|
+
const store = JsonEntityStore.from(prototypeOf(targetType), propertyKey);
|
|
17
|
+
if (store) {
|
|
18
|
+
setValue(error, "params.missingProperty", store.name || propertyKey);
|
|
19
|
+
return error.message?.replace(`'${propertyKey}'`, `'${store.name || propertyKey}'`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return error.message;
|
|
23
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getValue, nameOf } from "@tsed/core";
|
|
2
|
+
import { AjvValidationError } from "../errors/AjvValidationError.js";
|
|
3
|
+
import { getPath } from "./getPath.js";
|
|
4
|
+
import { mapClassError } from "./mapClassError.js";
|
|
5
|
+
/**
|
|
6
|
+
* Map raw AJV errors to framework-level validation error output.
|
|
7
|
+
*
|
|
8
|
+
* Enriches errors with collection/model information and contextual data values,
|
|
9
|
+
* then formats each message via the configured `errorFormatter`.
|
|
10
|
+
*
|
|
11
|
+
* @param errors Raw AJV error objects.
|
|
12
|
+
* @param options Validation context used to enrich error payloads.
|
|
13
|
+
* @returns Normalized Ts.ED validation error.
|
|
14
|
+
*/
|
|
15
|
+
export function mapErrors(errors, options) {
|
|
16
|
+
const { type, collectionType, value, errorFormatter } = options;
|
|
17
|
+
const message = errors
|
|
18
|
+
.map((error) => {
|
|
19
|
+
if (collectionType) {
|
|
20
|
+
error.collectionName = nameOf(collectionType);
|
|
21
|
+
}
|
|
22
|
+
const dataPath = getPath(error);
|
|
23
|
+
if (error.data === undefined) {
|
|
24
|
+
if (dataPath) {
|
|
25
|
+
error.data = getValue(value, dataPath.replace(/^\./, ""));
|
|
26
|
+
}
|
|
27
|
+
else if (error.schemaPath !== "#/required") {
|
|
28
|
+
error.data = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (dataPath && dataPath.match(/pwd|password|mdp|secret/i)) {
|
|
32
|
+
error.data = "[REDACTED]";
|
|
33
|
+
}
|
|
34
|
+
if (type) {
|
|
35
|
+
error.modelName = nameOf(type);
|
|
36
|
+
error.message = mapClassError(error, type);
|
|
37
|
+
}
|
|
38
|
+
return errorFormatter(error);
|
|
39
|
+
})
|
|
40
|
+
.join("\n");
|
|
41
|
+
return new AjvValidationError(message, errors);
|
|
42
|
+
}
|
|
@@ -9,7 +9,7 @@ import { type AjvValidateOptions } from "../services/AjvService.js";
|
|
|
9
9
|
* @typeParam TReturn Explicit return type override. When omitted, `TValue` is returned.
|
|
10
10
|
* @typeParam TValue Input value type.
|
|
11
11
|
* @param value Value to validate.
|
|
12
|
-
* @param options Validation options, a
|
|
12
|
+
* @param options Validation options, a `JsonSchema`, or a target type descriptor.
|
|
13
13
|
* @returns The validated value. If `returnsCoercedValues` is enabled, it may contain coerced values.
|
|
14
14
|
* @throws {AjvValidationError} When validation fails.
|
|
15
15
|
*/
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import "./Ajv.js";
|
|
2
2
|
import { Type } from "@tsed/core";
|
|
3
3
|
import { JsonSchema, JsonSchemaObject } from "@tsed/schema";
|
|
4
|
-
import { Ajv,
|
|
5
|
-
import {
|
|
6
|
-
import { AjvErrorObject, ErrorFormatter } from "../interfaces/AjvSettings.js";
|
|
4
|
+
import { Ajv, ValidateFunction } from "ajv";
|
|
5
|
+
import { ErrorFormatter } from "../interfaces/AjvSettings.js";
|
|
7
6
|
export interface AjvValidateOptions extends Record<string, any> {
|
|
8
7
|
schema?: JsonSchema | Partial<JsonSchemaObject>;
|
|
9
8
|
type?: Type<any> | any;
|
|
@@ -14,7 +13,9 @@ export declare class AjvService {
|
|
|
14
13
|
readonly name = "ajv";
|
|
15
14
|
protected errorFormatter: ErrorFormatter;
|
|
16
15
|
protected returnsCoercedValues: boolean | undefined;
|
|
16
|
+
protected hasLoadSchema: boolean;
|
|
17
17
|
protected ajv: Ajv;
|
|
18
|
+
protected validatorsBySchema: WeakMap<object, ValidateFunction<unknown>>;
|
|
18
19
|
/**
|
|
19
20
|
* Validate a value against a JSON schema, a model type, or explicit AJV options.
|
|
20
21
|
*
|
|
@@ -30,8 +31,18 @@ export declare class AjvService {
|
|
|
30
31
|
* @returns The validated value (possibly coerced when configured).
|
|
31
32
|
* @throws {AjvValidationError} When validation fails.
|
|
32
33
|
*/
|
|
33
|
-
validate<TReturn = never, TValue = unknown>(value: TValue, options: AjvValidateOptions
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
validate<TReturn = never, TValue = unknown>(value: TValue, options: AjvValidateOptions): Promise<[TReturn] extends [never] ? TValue : TReturn>;
|
|
35
|
+
/**
|
|
36
|
+
* Resolve and compile a validator for the provided schema input.
|
|
37
|
+
*
|
|
38
|
+
* The compiled validator is cached by schema object identity to avoid
|
|
39
|
+
* recompiling validators for repeated calls using the same schema instance.
|
|
40
|
+
*
|
|
41
|
+
* When `loadSchema` is configured, this method uses `compileAsync` to support
|
|
42
|
+
* asynchronous external reference resolution.
|
|
43
|
+
*
|
|
44
|
+
* @param schema Schema source as `JsonSchema` instance or plain JSON schema object.
|
|
45
|
+
* @returns Compiled AJV validate function.
|
|
46
|
+
*/
|
|
47
|
+
protected getValidator(input: Type | JsonSchema | JsonSchemaObject, additionalOptions: Record<string, any>): Promise<ValidateFunction<unknown>>;
|
|
37
48
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Type } from "@tsed/core";
|
|
2
|
+
import { AjvErrorObject } from "../interfaces/AjvSettings.js";
|
|
3
|
+
/**
|
|
4
|
+
* Rewrite class-related AJV messages using Ts.ED metadata.
|
|
5
|
+
*
|
|
6
|
+
* For missing required properties, this method replaces the property key by
|
|
7
|
+
* the store-resolved property name when aliasing/naming is configured.
|
|
8
|
+
*
|
|
9
|
+
* @param error AJV error object to transform.
|
|
10
|
+
* @param targetType Class used to resolve property metadata.
|
|
11
|
+
* @returns Updated message when metadata exists, otherwise original message.
|
|
12
|
+
*/
|
|
13
|
+
export declare function mapClassError(error: AjvErrorObject, targetType: Type<any>): string | undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Type } from "@tsed/core";
|
|
2
|
+
import { ErrorObject } from "ajv";
|
|
3
|
+
import { AjvValidationError } from "../errors/AjvValidationError.js";
|
|
4
|
+
import { ErrorFormatter } from "../interfaces/AjvSettings.js";
|
|
5
|
+
export interface MapErrorsOptions {
|
|
6
|
+
collectionType?: Type<any> | any;
|
|
7
|
+
errorFormatter: ErrorFormatter;
|
|
8
|
+
type?: Type<any> | any;
|
|
9
|
+
value: unknown;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Map raw AJV errors to framework-level validation error output.
|
|
13
|
+
*
|
|
14
|
+
* Enriches errors with collection/model information and contextual data values,
|
|
15
|
+
* then formats each message via the configured `errorFormatter`.
|
|
16
|
+
*
|
|
17
|
+
* @param errors Raw AJV error objects.
|
|
18
|
+
* @param options Validation context used to enrich error payloads.
|
|
19
|
+
* @returns Normalized Ts.ED validation error.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mapErrors(errors: ErrorObject[], options: MapErrorsOptions): AjvValidationError;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tsed/ajv",
|
|
3
3
|
"description": "AJV package for Ts.ED framework",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.
|
|
5
|
+
"version": "8.28.0",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
@@ -29,17 +29,17 @@
|
|
|
29
29
|
"tslib": ">=2.7.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@tsed/barrels": "8.
|
|
33
|
-
"@tsed/core": "8.
|
|
34
|
-
"@tsed/di": "8.
|
|
35
|
-
"@tsed/exceptions": "8.
|
|
36
|
-
"@tsed/schema": "8.
|
|
37
|
-
"@tsed/typescript": "8.
|
|
32
|
+
"@tsed/barrels": "8.28.0",
|
|
33
|
+
"@tsed/core": "8.28.0",
|
|
34
|
+
"@tsed/di": "8.28.0",
|
|
35
|
+
"@tsed/exceptions": "8.28.0",
|
|
36
|
+
"@tsed/schema": "8.28.0",
|
|
37
|
+
"@tsed/typescript": "8.28.0",
|
|
38
38
|
"ajv": "^8.17.1",
|
|
39
39
|
"eslint": "9.12.0",
|
|
40
40
|
"typescript": "6.0.2",
|
|
41
41
|
"vite": "7.3.2",
|
|
42
|
-
"vitest": "
|
|
42
|
+
"vitest": "4.1.4"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@tsed/core": ">=8.0.0",
|