@carno.js/core 1.0.2 → 1.0.4
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/LICENSE +21 -674
- package/README.md +188 -188
- package/dist/bun/index.js +7 -20
- package/dist/bun/index.js.map +28 -28
- package/dist/context/Context.js +2 -1
- package/dist/context/Context.mjs +2 -1
- package/dist/utils/parseQuery.js +84 -0
- package/dist/utils/parseQuery.mjs +63 -0
- package/package.json +3 -2
- package/src/Carno.ts +605 -605
- package/src/DefaultRoutes.ts +34 -34
- package/src/cache/CacheDriver.ts +50 -50
- package/src/cache/CacheService.ts +139 -139
- package/src/cache/MemoryDriver.ts +104 -104
- package/src/cache/RedisDriver.ts +116 -116
- package/src/compiler/JITCompiler.ts +167 -167
- package/src/container/Container.ts +168 -168
- package/src/context/Context.ts +130 -128
- package/src/cors/CorsHandler.ts +145 -145
- package/src/decorators/Controller.ts +63 -63
- package/src/decorators/Inject.ts +16 -16
- package/src/decorators/Middleware.ts +22 -22
- package/src/decorators/Service.ts +18 -18
- package/src/decorators/methods.ts +58 -58
- package/src/decorators/params.ts +47 -47
- package/src/events/Lifecycle.ts +97 -97
- package/src/exceptions/HttpException.ts +99 -99
- package/src/index.ts +92 -92
- package/src/metadata.ts +46 -46
- package/src/middleware/CarnoMiddleware.ts +14 -14
- package/src/router/RadixRouter.ts +225 -225
- package/src/testing/TestHarness.ts +177 -177
- package/src/utils/Metadata.ts +43 -43
- package/src/utils/parseQuery.ts +161 -0
- package/src/validation/ValibotAdapter.ts +95 -95
- package/src/validation/ValidatorAdapter.ts +69 -69
- package/src/validation/ZodAdapter.ts +102 -102
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
import type { ValidatorAdapter, ValidationResult, ValidationError } from './ValidatorAdapter';
|
|
2
|
-
import { VALIDATION_SCHEMA, getSchema } from './ValidatorAdapter';
|
|
3
|
-
import { ValidationException } from './ZodAdapter';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Valibot Adapter for Turbo validation.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import * as v from 'valibot';
|
|
11
|
-
*
|
|
12
|
-
* @Schema(v.object({
|
|
13
|
-
* name: v.pipe(v.string(), v.minLength(1)),
|
|
14
|
-
* email: v.pipe(v.string(), v.email())
|
|
15
|
-
* }))
|
|
16
|
-
* class CreateUserDto {
|
|
17
|
-
* name: string;
|
|
18
|
-
* email: string;
|
|
19
|
-
* }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export class ValibotAdapter implements ValidatorAdapter {
|
|
23
|
-
readonly name = 'ValibotAdapter';
|
|
24
|
-
|
|
25
|
-
private schemaCache = new Map<any, any>();
|
|
26
|
-
private valibot: any = null;
|
|
27
|
-
|
|
28
|
-
constructor() {
|
|
29
|
-
// Lazy load valibot
|
|
30
|
-
try {
|
|
31
|
-
this.valibot = require('valibot');
|
|
32
|
-
} catch {
|
|
33
|
-
// Will be loaded on first use
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private ensureValibot(): any {
|
|
38
|
-
if (!this.valibot) {
|
|
39
|
-
this.valibot = require('valibot');
|
|
40
|
-
}
|
|
41
|
-
return this.valibot;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
hasValidation(target: any): boolean {
|
|
45
|
-
return getSchema(target) !== undefined;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
validate<T>(target: any, value: unknown): ValidationResult<T> {
|
|
49
|
-
const schema = this.getOrCacheSchema(target);
|
|
50
|
-
|
|
51
|
-
if (!schema) {
|
|
52
|
-
return { success: true, data: value as T };
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const v = this.ensureValibot();
|
|
56
|
-
const result = v.safeParse(schema, value);
|
|
57
|
-
|
|
58
|
-
if (result.success) {
|
|
59
|
-
return { success: true, data: result.output };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
success: false,
|
|
64
|
-
errors: this.formatErrors(result.issues)
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
validateOrThrow<T>(target: any, value: unknown): T {
|
|
69
|
-
const result = this.validate<T>(target, value);
|
|
70
|
-
|
|
71
|
-
if (result.success) {
|
|
72
|
-
return result.data!;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
throw new ValidationException(result.errors!);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private getOrCacheSchema(target: any): any {
|
|
79
|
-
let schema = this.schemaCache.get(target);
|
|
80
|
-
|
|
81
|
-
if (schema === undefined) {
|
|
82
|
-
schema = getSchema(target) ?? null;
|
|
83
|
-
this.schemaCache.set(target, schema);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return schema;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private formatErrors(issues: any[]): ValidationError[] {
|
|
90
|
-
return issues.map((issue: any) => ({
|
|
91
|
-
path: issue.path?.map((p: any) => p.key).join('.') || '',
|
|
92
|
-
message: issue.message
|
|
93
|
-
}));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
1
|
+
import type { ValidatorAdapter, ValidationResult, ValidationError } from './ValidatorAdapter';
|
|
2
|
+
import { VALIDATION_SCHEMA, getSchema } from './ValidatorAdapter';
|
|
3
|
+
import { ValidationException } from './ZodAdapter';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Valibot Adapter for Turbo validation.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import * as v from 'valibot';
|
|
11
|
+
*
|
|
12
|
+
* @Schema(v.object({
|
|
13
|
+
* name: v.pipe(v.string(), v.minLength(1)),
|
|
14
|
+
* email: v.pipe(v.string(), v.email())
|
|
15
|
+
* }))
|
|
16
|
+
* class CreateUserDto {
|
|
17
|
+
* name: string;
|
|
18
|
+
* email: string;
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class ValibotAdapter implements ValidatorAdapter {
|
|
23
|
+
readonly name = 'ValibotAdapter';
|
|
24
|
+
|
|
25
|
+
private schemaCache = new Map<any, any>();
|
|
26
|
+
private valibot: any = null;
|
|
27
|
+
|
|
28
|
+
constructor() {
|
|
29
|
+
// Lazy load valibot
|
|
30
|
+
try {
|
|
31
|
+
this.valibot = require('valibot');
|
|
32
|
+
} catch {
|
|
33
|
+
// Will be loaded on first use
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private ensureValibot(): any {
|
|
38
|
+
if (!this.valibot) {
|
|
39
|
+
this.valibot = require('valibot');
|
|
40
|
+
}
|
|
41
|
+
return this.valibot;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
hasValidation(target: any): boolean {
|
|
45
|
+
return getSchema(target) !== undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
validate<T>(target: any, value: unknown): ValidationResult<T> {
|
|
49
|
+
const schema = this.getOrCacheSchema(target);
|
|
50
|
+
|
|
51
|
+
if (!schema) {
|
|
52
|
+
return { success: true, data: value as T };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const v = this.ensureValibot();
|
|
56
|
+
const result = v.safeParse(schema, value);
|
|
57
|
+
|
|
58
|
+
if (result.success) {
|
|
59
|
+
return { success: true, data: result.output };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
errors: this.formatErrors(result.issues)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validateOrThrow<T>(target: any, value: unknown): T {
|
|
69
|
+
const result = this.validate<T>(target, value);
|
|
70
|
+
|
|
71
|
+
if (result.success) {
|
|
72
|
+
return result.data!;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
throw new ValidationException(result.errors!);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private getOrCacheSchema(target: any): any {
|
|
79
|
+
let schema = this.schemaCache.get(target);
|
|
80
|
+
|
|
81
|
+
if (schema === undefined) {
|
|
82
|
+
schema = getSchema(target) ?? null;
|
|
83
|
+
this.schemaCache.set(target, schema);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return schema;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private formatErrors(issues: any[]): ValidationError[] {
|
|
90
|
+
return issues.map((issue: any) => ({
|
|
91
|
+
path: issue.path?.map((p: any) => p.key).join('.') || '',
|
|
92
|
+
message: issue.message
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation result type.
|
|
3
|
-
*/
|
|
4
|
-
export interface ValidationResult<T = any> {
|
|
5
|
-
success: boolean;
|
|
6
|
-
data?: T;
|
|
7
|
-
errors?: ValidationError[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ValidationError {
|
|
11
|
-
path: string;
|
|
12
|
-
message: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Base interface for validation adapters.
|
|
17
|
-
* Adapters provide validation capabilities for different libraries.
|
|
18
|
-
*/
|
|
19
|
-
export interface ValidatorAdapter {
|
|
20
|
-
/**
|
|
21
|
-
* Validator name for debugging.
|
|
22
|
-
*/
|
|
23
|
-
readonly name: string;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Check if a target has validation schema.
|
|
27
|
-
*/
|
|
28
|
-
hasValidation(target: any): boolean;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Validate and transform a value.
|
|
32
|
-
* Returns result object instead of throwing for better performance.
|
|
33
|
-
*/
|
|
34
|
-
validate<T>(target: any, value: unknown): ValidationResult<T>;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Validate and transform, throwing on error.
|
|
38
|
-
* Used when you want to short-circuit on failure.
|
|
39
|
-
*/
|
|
40
|
-
validateOrThrow<T>(target: any, value: unknown): T;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Validation configuration for Turbo.
|
|
45
|
-
*/
|
|
46
|
-
export interface ValidationConfig {
|
|
47
|
-
adapter: ValidatorAdapter;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Symbol for storing validation schema on DTOs.
|
|
52
|
-
*/
|
|
53
|
-
export const VALIDATION_SCHEMA = Symbol('turbo:validation');
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Decorator to attach a validation schema to a DTO class.
|
|
57
|
-
*/
|
|
58
|
-
export function Schema(schema: any): ClassDecorator {
|
|
59
|
-
return (target) => {
|
|
60
|
-
Reflect.defineMetadata(VALIDATION_SCHEMA, schema, target);
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Get the validation schema from a DTO class.
|
|
66
|
-
*/
|
|
67
|
-
export function getSchema(target: any): any | undefined {
|
|
68
|
-
return Reflect.getMetadata(VALIDATION_SCHEMA, target);
|
|
69
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Validation result type.
|
|
3
|
+
*/
|
|
4
|
+
export interface ValidationResult<T = any> {
|
|
5
|
+
success: boolean;
|
|
6
|
+
data?: T;
|
|
7
|
+
errors?: ValidationError[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ValidationError {
|
|
11
|
+
path: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Base interface for validation adapters.
|
|
17
|
+
* Adapters provide validation capabilities for different libraries.
|
|
18
|
+
*/
|
|
19
|
+
export interface ValidatorAdapter {
|
|
20
|
+
/**
|
|
21
|
+
* Validator name for debugging.
|
|
22
|
+
*/
|
|
23
|
+
readonly name: string;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a target has validation schema.
|
|
27
|
+
*/
|
|
28
|
+
hasValidation(target: any): boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate and transform a value.
|
|
32
|
+
* Returns result object instead of throwing for better performance.
|
|
33
|
+
*/
|
|
34
|
+
validate<T>(target: any, value: unknown): ValidationResult<T>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate and transform, throwing on error.
|
|
38
|
+
* Used when you want to short-circuit on failure.
|
|
39
|
+
*/
|
|
40
|
+
validateOrThrow<T>(target: any, value: unknown): T;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validation configuration for Turbo.
|
|
45
|
+
*/
|
|
46
|
+
export interface ValidationConfig {
|
|
47
|
+
adapter: ValidatorAdapter;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Symbol for storing validation schema on DTOs.
|
|
52
|
+
*/
|
|
53
|
+
export const VALIDATION_SCHEMA = Symbol('turbo:validation');
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Decorator to attach a validation schema to a DTO class.
|
|
57
|
+
*/
|
|
58
|
+
export function Schema(schema: any): ClassDecorator {
|
|
59
|
+
return (target) => {
|
|
60
|
+
Reflect.defineMetadata(VALIDATION_SCHEMA, schema, target);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the validation schema from a DTO class.
|
|
66
|
+
*/
|
|
67
|
+
export function getSchema(target: any): any | undefined {
|
|
68
|
+
return Reflect.getMetadata(VALIDATION_SCHEMA, target);
|
|
69
|
+
}
|
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
import type { ValidatorAdapter, ValidationResult, ValidationError } from './ValidatorAdapter';
|
|
2
|
-
import { VALIDATION_SCHEMA, getSchema } from './ValidatorAdapter';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Zod Adapter - Default validation adapter for Turbo.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* ```typescript
|
|
9
|
-
* import { z } from 'zod';
|
|
10
|
-
*
|
|
11
|
-
* @Schema(z.object({
|
|
12
|
-
* name: z.string().min(1),
|
|
13
|
-
* email: z.string().email()
|
|
14
|
-
* }))
|
|
15
|
-
* class CreateUserDto {
|
|
16
|
-
* name: string;
|
|
17
|
-
* email: string;
|
|
18
|
-
* }
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export class ZodAdapter implements ValidatorAdapter {
|
|
22
|
-
readonly name = 'ZodAdapter';
|
|
23
|
-
|
|
24
|
-
// Cache parsed schemas for performance
|
|
25
|
-
private schemaCache = new Map<any, any>();
|
|
26
|
-
|
|
27
|
-
hasValidation(target: any): boolean {
|
|
28
|
-
return getSchema(target) !== undefined;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
validate<T>(target: any, value: unknown): ValidationResult<T> {
|
|
32
|
-
const schema = this.getOrCacheSchema(target);
|
|
33
|
-
|
|
34
|
-
if (!schema) {
|
|
35
|
-
return { success: true, data: value as T };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const result = schema.safeParse(value);
|
|
39
|
-
|
|
40
|
-
if (result.success) {
|
|
41
|
-
return { success: true, data: result.data };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
success: false,
|
|
46
|
-
errors: this.formatErrors(result.error)
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
validateOrThrow<T>(target: any, value: unknown): T {
|
|
51
|
-
const schema = this.getOrCacheSchema(target);
|
|
52
|
-
|
|
53
|
-
if (!schema) {
|
|
54
|
-
return value as T;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const result = schema.safeParse(value);
|
|
58
|
-
|
|
59
|
-
if (result.success) {
|
|
60
|
-
return result.data;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const errors = this.formatErrors(result.error);
|
|
64
|
-
throw new ValidationException(errors);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private getOrCacheSchema(target: any): any {
|
|
68
|
-
let schema = this.schemaCache.get(target);
|
|
69
|
-
|
|
70
|
-
if (schema === undefined) {
|
|
71
|
-
schema = getSchema(target) ?? null;
|
|
72
|
-
this.schemaCache.set(target, schema);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return schema;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
private formatErrors(zodError: any): ValidationError[] {
|
|
79
|
-
return zodError.issues.map((issue: any) => ({
|
|
80
|
-
path: issue.path.join('.'),
|
|
81
|
-
message: issue.message
|
|
82
|
-
}));
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Validation exception thrown when validation fails.
|
|
88
|
-
*/
|
|
89
|
-
export class ValidationException extends Error {
|
|
90
|
-
constructor(public readonly errors: ValidationError[]) {
|
|
91
|
-
super(`Validation failed: ${errors.map(e => `${e.path}: ${e.message}`).join(', ')}`);
|
|
92
|
-
this.name = 'ValidationException';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
toResponse(): Response {
|
|
96
|
-
return Response.json({
|
|
97
|
-
statusCode: 400,
|
|
98
|
-
message: 'Validation failed',
|
|
99
|
-
errors: this.errors
|
|
100
|
-
}, { status: 400 });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
1
|
+
import type { ValidatorAdapter, ValidationResult, ValidationError } from './ValidatorAdapter';
|
|
2
|
+
import { VALIDATION_SCHEMA, getSchema } from './ValidatorAdapter';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Zod Adapter - Default validation adapter for Turbo.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { z } from 'zod';
|
|
10
|
+
*
|
|
11
|
+
* @Schema(z.object({
|
|
12
|
+
* name: z.string().min(1),
|
|
13
|
+
* email: z.string().email()
|
|
14
|
+
* }))
|
|
15
|
+
* class CreateUserDto {
|
|
16
|
+
* name: string;
|
|
17
|
+
* email: string;
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export class ZodAdapter implements ValidatorAdapter {
|
|
22
|
+
readonly name = 'ZodAdapter';
|
|
23
|
+
|
|
24
|
+
// Cache parsed schemas for performance
|
|
25
|
+
private schemaCache = new Map<any, any>();
|
|
26
|
+
|
|
27
|
+
hasValidation(target: any): boolean {
|
|
28
|
+
return getSchema(target) !== undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
validate<T>(target: any, value: unknown): ValidationResult<T> {
|
|
32
|
+
const schema = this.getOrCacheSchema(target);
|
|
33
|
+
|
|
34
|
+
if (!schema) {
|
|
35
|
+
return { success: true, data: value as T };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const result = schema.safeParse(value);
|
|
39
|
+
|
|
40
|
+
if (result.success) {
|
|
41
|
+
return { success: true, data: result.data };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
success: false,
|
|
46
|
+
errors: this.formatErrors(result.error)
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
validateOrThrow<T>(target: any, value: unknown): T {
|
|
51
|
+
const schema = this.getOrCacheSchema(target);
|
|
52
|
+
|
|
53
|
+
if (!schema) {
|
|
54
|
+
return value as T;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const result = schema.safeParse(value);
|
|
58
|
+
|
|
59
|
+
if (result.success) {
|
|
60
|
+
return result.data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const errors = this.formatErrors(result.error);
|
|
64
|
+
throw new ValidationException(errors);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private getOrCacheSchema(target: any): any {
|
|
68
|
+
let schema = this.schemaCache.get(target);
|
|
69
|
+
|
|
70
|
+
if (schema === undefined) {
|
|
71
|
+
schema = getSchema(target) ?? null;
|
|
72
|
+
this.schemaCache.set(target, schema);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return schema;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private formatErrors(zodError: any): ValidationError[] {
|
|
79
|
+
return zodError.issues.map((issue: any) => ({
|
|
80
|
+
path: issue.path.join('.'),
|
|
81
|
+
message: issue.message
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Validation exception thrown when validation fails.
|
|
88
|
+
*/
|
|
89
|
+
export class ValidationException extends Error {
|
|
90
|
+
constructor(public readonly errors: ValidationError[]) {
|
|
91
|
+
super(`Validation failed: ${errors.map(e => `${e.path}: ${e.message}`).join(', ')}`);
|
|
92
|
+
this.name = 'ValidationException';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
toResponse(): Response {
|
|
96
|
+
return Response.json({
|
|
97
|
+
statusCode: 400,
|
|
98
|
+
message: 'Validation failed',
|
|
99
|
+
errors: this.errors
|
|
100
|
+
}, { status: 400 });
|
|
101
|
+
}
|
|
102
|
+
}
|