wynkjs 1.0.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/LICENSE +21 -0
- package/README.md +522 -0
- package/dist/database.d.ts +36 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +162 -0
- package/dist/decorators/database.decorators.d.ts +55 -0
- package/dist/decorators/database.decorators.d.ts.map +1 -0
- package/dist/decorators/database.decorators.js +131 -0
- package/dist/decorators/exception.advanced.d.ts +160 -0
- package/dist/decorators/exception.advanced.d.ts.map +1 -0
- package/dist/decorators/exception.advanced.js +232 -0
- package/dist/decorators/exception.decorators.d.ts +121 -0
- package/dist/decorators/exception.decorators.d.ts.map +1 -0
- package/dist/decorators/exception.decorators.js +242 -0
- package/dist/decorators/guard.decorators.d.ts +43 -0
- package/dist/decorators/guard.decorators.d.ts.map +1 -0
- package/dist/decorators/guard.decorators.js +67 -0
- package/dist/decorators/http.decorators.d.ts +130 -0
- package/dist/decorators/http.decorators.d.ts.map +1 -0
- package/dist/decorators/http.decorators.js +209 -0
- package/dist/decorators/interceptor.advanced.d.ts +93 -0
- package/dist/decorators/interceptor.advanced.d.ts.map +1 -0
- package/dist/decorators/interceptor.advanced.js +228 -0
- package/dist/decorators/interceptor.decorators.d.ts +91 -0
- package/dist/decorators/interceptor.decorators.d.ts.map +1 -0
- package/dist/decorators/interceptor.decorators.js +163 -0
- package/dist/decorators/param.decorators.d.ts +144 -0
- package/dist/decorators/param.decorators.d.ts.map +1 -0
- package/dist/decorators/param.decorators.js +205 -0
- package/dist/decorators/pipe.advanced.d.ts +125 -0
- package/dist/decorators/pipe.advanced.d.ts.map +1 -0
- package/dist/decorators/pipe.advanced.js +263 -0
- package/dist/decorators/pipe.decorators.d.ts +226 -0
- package/dist/decorators/pipe.decorators.d.ts.map +1 -0
- package/dist/decorators/pipe.decorators.js +420 -0
- package/dist/dto.d.ts +83 -0
- package/dist/dto.d.ts.map +1 -0
- package/dist/dto.js +88 -0
- package/dist/factory.d.ts +76 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/factory.js +410 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/pipes/validation.pipe.d.ts +91 -0
- package/dist/pipes/validation.pipe.d.ts.map +1 -0
- package/dist/pipes/validation.pipe.js +163 -0
- package/package.json +68 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
/**
|
|
3
|
+
* Helper to register parameter metadata
|
|
4
|
+
*/
|
|
5
|
+
function createParamDecorator(type, data, pipes) {
|
|
6
|
+
return (target, propertyKey, parameterIndex) => {
|
|
7
|
+
if (!propertyKey)
|
|
8
|
+
return;
|
|
9
|
+
const existingParams = Reflect.getMetadata("params", target, propertyKey) || [];
|
|
10
|
+
existingParams.push({
|
|
11
|
+
index: parameterIndex,
|
|
12
|
+
type,
|
|
13
|
+
data,
|
|
14
|
+
pipes,
|
|
15
|
+
});
|
|
16
|
+
Reflect.defineMetadata("params", existingParams, target, propertyKey);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @Body decorator - Extracts request body
|
|
21
|
+
* @param property Optional property name to extract from body
|
|
22
|
+
* @param pipes Optional validation/transformation pipes
|
|
23
|
+
* @example
|
|
24
|
+
* @Post()
|
|
25
|
+
* create(@Body() dto: CreateDto) {}
|
|
26
|
+
*
|
|
27
|
+
* @Post()
|
|
28
|
+
* update(@Body('name') name: string) {}
|
|
29
|
+
*/
|
|
30
|
+
export function Body(property, ...pipes) {
|
|
31
|
+
return createParamDecorator("body", property, pipes);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @Param decorator - Extracts route parameters
|
|
35
|
+
* @param property Optional parameter name
|
|
36
|
+
* @param pipes Optional validation/transformation pipes
|
|
37
|
+
* @example
|
|
38
|
+
* @Get('/:id')
|
|
39
|
+
* findOne(@Param('id') id: string) {}
|
|
40
|
+
*
|
|
41
|
+
* @Get('/:id')
|
|
42
|
+
* findOne(@Param() params: any) {}
|
|
43
|
+
*/
|
|
44
|
+
export function Param(property, ...pipes) {
|
|
45
|
+
return createParamDecorator("param", property, pipes);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @Query decorator - Extracts query parameters
|
|
49
|
+
* @param property Optional query parameter name
|
|
50
|
+
* @param pipes Optional validation/transformation pipes
|
|
51
|
+
* @example
|
|
52
|
+
* @Get()
|
|
53
|
+
* findAll(@Query('page') page: number) {}
|
|
54
|
+
*
|
|
55
|
+
* @Get()
|
|
56
|
+
* findAll(@Query() query: QueryDto) {}
|
|
57
|
+
*/
|
|
58
|
+
export function Query(property, ...pipes) {
|
|
59
|
+
return createParamDecorator("query", property, pipes);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @Headers decorator - Extracts request headers
|
|
63
|
+
* @param property Optional header name
|
|
64
|
+
* @example
|
|
65
|
+
* @Get()
|
|
66
|
+
* getData(@Headers('authorization') auth: string) {}
|
|
67
|
+
*
|
|
68
|
+
* @Get()
|
|
69
|
+
* getData(@Headers() headers: any) {}
|
|
70
|
+
*/
|
|
71
|
+
export function Headers(property) {
|
|
72
|
+
return createParamDecorator("headers", property);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @Req decorator - Injects full request object
|
|
76
|
+
* @example
|
|
77
|
+
* @Get()
|
|
78
|
+
* getData(@Req() request: Request) {}
|
|
79
|
+
*/
|
|
80
|
+
export function Req() {
|
|
81
|
+
return createParamDecorator("request");
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* @Request decorator - Alias for @Req
|
|
85
|
+
*/
|
|
86
|
+
export const Request = Req;
|
|
87
|
+
/**
|
|
88
|
+
* @Res decorator - Injects response object
|
|
89
|
+
* @example
|
|
90
|
+
* @Get()
|
|
91
|
+
* getData(@Res() response: Response) {}
|
|
92
|
+
*/
|
|
93
|
+
export function Res() {
|
|
94
|
+
return createParamDecorator("response");
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* @Response decorator - Alias for @Res
|
|
98
|
+
*/
|
|
99
|
+
export const Response = Res;
|
|
100
|
+
/**
|
|
101
|
+
* @Context decorator - Injects full Elysia context
|
|
102
|
+
* @example
|
|
103
|
+
* @Get()
|
|
104
|
+
* getData(@Context() ctx: any) {}
|
|
105
|
+
*/
|
|
106
|
+
export function Context() {
|
|
107
|
+
return createParamDecorator("context");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @User decorator - Extracts user from context (after authentication)
|
|
111
|
+
* @param property Optional user property to extract
|
|
112
|
+
* @example
|
|
113
|
+
* @Get('/profile')
|
|
114
|
+
* @UseGuards(AuthGuard)
|
|
115
|
+
* getProfile(@User() user: UserEntity) {}
|
|
116
|
+
*
|
|
117
|
+
* @Get('/profile')
|
|
118
|
+
* getProfile(@User('id') userId: string) {}
|
|
119
|
+
*/
|
|
120
|
+
export function User(property) {
|
|
121
|
+
return createParamDecorator("user", property);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* @UploadedFile decorator - Extracts uploaded file
|
|
125
|
+
* @example
|
|
126
|
+
* @Post('/upload')
|
|
127
|
+
* uploadFile(@UploadedFile() file: File) {}
|
|
128
|
+
*/
|
|
129
|
+
export function UploadedFile() {
|
|
130
|
+
return createParamDecorator("file");
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* @UploadedFiles decorator - Extracts multiple uploaded files
|
|
134
|
+
* @example
|
|
135
|
+
* @Post('/upload-multiple')
|
|
136
|
+
* uploadFiles(@UploadedFiles() files: File[]) {}
|
|
137
|
+
*/
|
|
138
|
+
export function UploadedFiles() {
|
|
139
|
+
return createParamDecorator("files");
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* @Ip decorator - Extracts client IP address
|
|
143
|
+
* @example
|
|
144
|
+
* @Get()
|
|
145
|
+
* getData(@Ip() ip: string) {}
|
|
146
|
+
*/
|
|
147
|
+
export function Ip() {
|
|
148
|
+
return (target, propertyKey, parameterIndex) => {
|
|
149
|
+
if (!propertyKey)
|
|
150
|
+
return;
|
|
151
|
+
const existingParams = Reflect.getMetadata("params", target, propertyKey) || [];
|
|
152
|
+
existingParams.push({
|
|
153
|
+
index: parameterIndex,
|
|
154
|
+
type: "headers",
|
|
155
|
+
data: "x-forwarded-for",
|
|
156
|
+
});
|
|
157
|
+
Reflect.defineMetadata("params", existingParams, target, propertyKey);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* @Session decorator - Extracts session data
|
|
162
|
+
* @param property Optional session property name
|
|
163
|
+
* @example
|
|
164
|
+
* @Get()
|
|
165
|
+
* getData(@Session() session: any) {}
|
|
166
|
+
*
|
|
167
|
+
* @Get()
|
|
168
|
+
* getData(@Session('userId') userId: string) {}
|
|
169
|
+
*/
|
|
170
|
+
export function Session(property) {
|
|
171
|
+
return (target, propertyKey, parameterIndex) => {
|
|
172
|
+
if (!propertyKey)
|
|
173
|
+
return;
|
|
174
|
+
const existingParams = Reflect.getMetadata("params", target, propertyKey) || [];
|
|
175
|
+
existingParams.push({
|
|
176
|
+
index: parameterIndex,
|
|
177
|
+
type: "context",
|
|
178
|
+
data: property ? `session.${property}` : "session",
|
|
179
|
+
});
|
|
180
|
+
Reflect.defineMetadata("params", existingParams, target, propertyKey);
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* @HostParam decorator - Extracts subdomain parameters
|
|
185
|
+
* @param property Host parameter name
|
|
186
|
+
* @example
|
|
187
|
+
* @Controller({ host: ':account.example.com' })
|
|
188
|
+
* export class AccountController {
|
|
189
|
+
* @Get()
|
|
190
|
+
* getData(@HostParam('account') account: string) {}
|
|
191
|
+
* }
|
|
192
|
+
*/
|
|
193
|
+
export function HostParam(property) {
|
|
194
|
+
return (target, propertyKey, parameterIndex) => {
|
|
195
|
+
if (!propertyKey)
|
|
196
|
+
return;
|
|
197
|
+
const existingParams = Reflect.getMetadata("params", target, propertyKey) || [];
|
|
198
|
+
existingParams.push({
|
|
199
|
+
index: parameterIndex,
|
|
200
|
+
type: "context",
|
|
201
|
+
data: `host.${property}`,
|
|
202
|
+
});
|
|
203
|
+
Reflect.defineMetadata("params", existingParams, target, propertyKey);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { WynkPipeTransform, ArgumentMetadata } from "./pipe.decorators";
|
|
3
|
+
/**
|
|
4
|
+
* Advanced Pipes for WynkJS Framework
|
|
5
|
+
* Additional pipes for common transformations and validations
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse Date Pipe - Converts string to Date object
|
|
9
|
+
* @example
|
|
10
|
+
* @Get('/:date')
|
|
11
|
+
* async getByDate(@Param('date', ParseDatePipe) date: Date) {}
|
|
12
|
+
*/
|
|
13
|
+
export declare class ParseDatePipe implements WynkPipeTransform<string, Date> {
|
|
14
|
+
transform(value: string, metadata?: ArgumentMetadata): Date;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Parse File Pipe - Validates and transforms file uploads
|
|
18
|
+
* @example
|
|
19
|
+
* @Post('/upload')
|
|
20
|
+
* async uploadFile(@UploadedFile(ParseFilePipe) file: any) {}
|
|
21
|
+
*/
|
|
22
|
+
export declare class ParseFilePipe implements WynkPipeTransform<any, any> {
|
|
23
|
+
private options?;
|
|
24
|
+
constructor(options?: {
|
|
25
|
+
maxSize?: number;
|
|
26
|
+
allowedTypes?: string[];
|
|
27
|
+
required?: boolean;
|
|
28
|
+
} | undefined);
|
|
29
|
+
transform(value: any, metadata?: ArgumentMetadata): any;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sanitize Pipe - Sanitizes input by removing dangerous characters
|
|
33
|
+
* @example
|
|
34
|
+
* @Post()
|
|
35
|
+
* async create(@Body(SanitizePipe) data: any) {}
|
|
36
|
+
*/
|
|
37
|
+
export declare class SanitizePipe implements WynkPipeTransform {
|
|
38
|
+
private dangerousPatterns;
|
|
39
|
+
transform(value: any, metadata?: ArgumentMetadata): any;
|
|
40
|
+
private sanitizeString;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Transform Case Pipe - Transforms string case
|
|
44
|
+
* @example
|
|
45
|
+
* @Post()
|
|
46
|
+
* async create(@Body('email', new TransformCasePipe('lower')) email: string) {}
|
|
47
|
+
*/
|
|
48
|
+
export declare class TransformCasePipe implements WynkPipeTransform<string, string> {
|
|
49
|
+
private caseType;
|
|
50
|
+
constructor(caseType?: "lower" | "upper" | "title");
|
|
51
|
+
transform(value: string, metadata?: ArgumentMetadata): string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse JSON Pipe - Parses JSON strings
|
|
55
|
+
* @example
|
|
56
|
+
* @Post()
|
|
57
|
+
* async create(@Body('metadata', ParseJSONPipe) metadata: any) {}
|
|
58
|
+
*/
|
|
59
|
+
export declare class ParseJSONPipe implements WynkPipeTransform<string, any> {
|
|
60
|
+
transform(value: string, metadata?: ArgumentMetadata): any;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Validate Email Pipe - Validates email format
|
|
64
|
+
* @example
|
|
65
|
+
* @Post()
|
|
66
|
+
* async create(@Body('email', ValidateEmailPipe) email: string) {}
|
|
67
|
+
*/
|
|
68
|
+
export declare class ValidateEmailPipe implements WynkPipeTransform<string, string> {
|
|
69
|
+
private emailRegex;
|
|
70
|
+
transform(value: string, metadata?: ArgumentMetadata): string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Validate Length Pipe - Validates string/array length
|
|
74
|
+
* @example
|
|
75
|
+
* @Post()
|
|
76
|
+
* async create(@Body('username', new ValidateLengthPipe(3, 20)) username: string) {}
|
|
77
|
+
*/
|
|
78
|
+
export declare class ValidateLengthPipe implements WynkPipeTransform {
|
|
79
|
+
private min?;
|
|
80
|
+
private max?;
|
|
81
|
+
constructor(min?: number | undefined, max?: number | undefined);
|
|
82
|
+
transform(value: any, metadata?: ArgumentMetadata): any;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validate Range Pipe - Validates number is within range
|
|
86
|
+
* @example
|
|
87
|
+
* @Get()
|
|
88
|
+
* async getData(@Query('page', new ValidateRangePipe(1, 100)) page: number) {}
|
|
89
|
+
*/
|
|
90
|
+
export declare class ValidateRangePipe implements WynkPipeTransform<number, number> {
|
|
91
|
+
private min?;
|
|
92
|
+
private max?;
|
|
93
|
+
constructor(min?: number | undefined, max?: number | undefined);
|
|
94
|
+
transform(value: number, metadata?: ArgumentMetadata): number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Strip HTML Pipe - Removes HTML tags from string
|
|
98
|
+
* @example
|
|
99
|
+
* @Post()
|
|
100
|
+
* async create(@Body('comment', StripHTMLPipe) comment: string) {}
|
|
101
|
+
*/
|
|
102
|
+
export declare class StripHTMLPipe implements WynkPipeTransform<string, string> {
|
|
103
|
+
transform(value: string, metadata?: ArgumentMetadata): string;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Slugify Pipe - Converts string to URL-friendly slug
|
|
107
|
+
* @example
|
|
108
|
+
* @Post()
|
|
109
|
+
* async create(@Body('title', SlugifyPipe) slug: string) {}
|
|
110
|
+
*/
|
|
111
|
+
export declare class SlugifyPipe implements WynkPipeTransform<string, string> {
|
|
112
|
+
transform(value: string, metadata?: ArgumentMetadata): string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Parse Comma Separated Pipe - Converts comma-separated string to array
|
|
116
|
+
* @example
|
|
117
|
+
* @Get()
|
|
118
|
+
* async search(@Query('tags', ParseCommaSeparatedPipe) tags: string[]) {}
|
|
119
|
+
*/
|
|
120
|
+
export declare class ParseCommaSeparatedPipe implements WynkPipeTransform<string, string[]> {
|
|
121
|
+
private trim;
|
|
122
|
+
constructor(trim?: boolean);
|
|
123
|
+
transform(value: string, metadata?: ArgumentMetadata): string[];
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=pipe.advanced.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe.advanced.d.ts","sourceRoot":"","sources":["../../core/decorators/pipe.advanced.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { BadRequestException } from "./exception.decorators";
|
|
3
|
+
/**
|
|
4
|
+
* Advanced Pipes for WynkJS Framework
|
|
5
|
+
* Additional pipes for common transformations and validations
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse Date Pipe - Converts string to Date object
|
|
9
|
+
* @example
|
|
10
|
+
* @Get('/:date')
|
|
11
|
+
* async getByDate(@Param('date', ParseDatePipe) date: Date) {}
|
|
12
|
+
*/
|
|
13
|
+
export class ParseDatePipe {
|
|
14
|
+
transform(value, metadata) {
|
|
15
|
+
if (!value) {
|
|
16
|
+
throw new BadRequestException("Date value is required");
|
|
17
|
+
}
|
|
18
|
+
const date = new Date(value);
|
|
19
|
+
if (isNaN(date.getTime())) {
|
|
20
|
+
throw new BadRequestException(`"${value}" is not a valid date`);
|
|
21
|
+
}
|
|
22
|
+
return date;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse File Pipe - Validates and transforms file uploads
|
|
27
|
+
* @example
|
|
28
|
+
* @Post('/upload')
|
|
29
|
+
* async uploadFile(@UploadedFile(ParseFilePipe) file: any) {}
|
|
30
|
+
*/
|
|
31
|
+
export class ParseFilePipe {
|
|
32
|
+
options;
|
|
33
|
+
constructor(options) {
|
|
34
|
+
this.options = options;
|
|
35
|
+
}
|
|
36
|
+
transform(value, metadata) {
|
|
37
|
+
if (!value) {
|
|
38
|
+
if (this.options?.required) {
|
|
39
|
+
throw new BadRequestException("File is required");
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// Check file size
|
|
44
|
+
if (this.options?.maxSize && value.size > this.options.maxSize) {
|
|
45
|
+
const maxSizeMB = (this.options.maxSize / (1024 * 1024)).toFixed(2);
|
|
46
|
+
throw new BadRequestException(`File size exceeds ${maxSizeMB}MB limit`);
|
|
47
|
+
}
|
|
48
|
+
// Check file type
|
|
49
|
+
if (this.options?.allowedTypes &&
|
|
50
|
+
!this.options.allowedTypes.includes(value.type)) {
|
|
51
|
+
throw new BadRequestException(`File type must be one of: ${this.options.allowedTypes.join(", ")}`);
|
|
52
|
+
}
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sanitize Pipe - Sanitizes input by removing dangerous characters
|
|
58
|
+
* @example
|
|
59
|
+
* @Post()
|
|
60
|
+
* async create(@Body(SanitizePipe) data: any) {}
|
|
61
|
+
*/
|
|
62
|
+
export class SanitizePipe {
|
|
63
|
+
dangerousPatterns = [
|
|
64
|
+
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
|
|
65
|
+
/javascript:/gi,
|
|
66
|
+
/on\w+\s*=/gi,
|
|
67
|
+
];
|
|
68
|
+
transform(value, metadata) {
|
|
69
|
+
if (typeof value === "string") {
|
|
70
|
+
return this.sanitizeString(value);
|
|
71
|
+
}
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
return value.map((item) => this.transform(item, metadata));
|
|
74
|
+
}
|
|
75
|
+
if (value && typeof value === "object") {
|
|
76
|
+
const sanitized = {};
|
|
77
|
+
for (const key in value) {
|
|
78
|
+
sanitized[key] = this.transform(value[key], metadata);
|
|
79
|
+
}
|
|
80
|
+
return sanitized;
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
sanitizeString(str) {
|
|
85
|
+
let sanitized = str;
|
|
86
|
+
for (const pattern of this.dangerousPatterns) {
|
|
87
|
+
sanitized = sanitized.replace(pattern, "");
|
|
88
|
+
}
|
|
89
|
+
return sanitized;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Transform Case Pipe - Transforms string case
|
|
94
|
+
* @example
|
|
95
|
+
* @Post()
|
|
96
|
+
* async create(@Body('email', new TransformCasePipe('lower')) email: string) {}
|
|
97
|
+
*/
|
|
98
|
+
export class TransformCasePipe {
|
|
99
|
+
caseType;
|
|
100
|
+
constructor(caseType = "lower") {
|
|
101
|
+
this.caseType = caseType;
|
|
102
|
+
}
|
|
103
|
+
transform(value, metadata) {
|
|
104
|
+
if (typeof value !== "string") {
|
|
105
|
+
return value;
|
|
106
|
+
}
|
|
107
|
+
switch (this.caseType) {
|
|
108
|
+
case "lower":
|
|
109
|
+
return value.toLowerCase();
|
|
110
|
+
case "upper":
|
|
111
|
+
return value.toUpperCase();
|
|
112
|
+
case "title":
|
|
113
|
+
return value.replace(/\w\S*/g, (txt) => {
|
|
114
|
+
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
|
115
|
+
});
|
|
116
|
+
default:
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Parse JSON Pipe - Parses JSON strings
|
|
123
|
+
* @example
|
|
124
|
+
* @Post()
|
|
125
|
+
* async create(@Body('metadata', ParseJSONPipe) metadata: any) {}
|
|
126
|
+
*/
|
|
127
|
+
export class ParseJSONPipe {
|
|
128
|
+
transform(value, metadata) {
|
|
129
|
+
if (!value || typeof value !== "string") {
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(value);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throw new BadRequestException("Invalid JSON format");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Validate Email Pipe - Validates email format
|
|
142
|
+
* @example
|
|
143
|
+
* @Post()
|
|
144
|
+
* async create(@Body('email', ValidateEmailPipe) email: string) {}
|
|
145
|
+
*/
|
|
146
|
+
export class ValidateEmailPipe {
|
|
147
|
+
emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
148
|
+
transform(value, metadata) {
|
|
149
|
+
if (!value) {
|
|
150
|
+
throw new BadRequestException("Email is required");
|
|
151
|
+
}
|
|
152
|
+
if (!this.emailRegex.test(value)) {
|
|
153
|
+
throw new BadRequestException("Invalid email format");
|
|
154
|
+
}
|
|
155
|
+
return value.toLowerCase();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Validate Length Pipe - Validates string/array length
|
|
160
|
+
* @example
|
|
161
|
+
* @Post()
|
|
162
|
+
* async create(@Body('username', new ValidateLengthPipe(3, 20)) username: string) {}
|
|
163
|
+
*/
|
|
164
|
+
export class ValidateLengthPipe {
|
|
165
|
+
min;
|
|
166
|
+
max;
|
|
167
|
+
constructor(min, max) {
|
|
168
|
+
this.min = min;
|
|
169
|
+
this.max = max;
|
|
170
|
+
}
|
|
171
|
+
transform(value, metadata) {
|
|
172
|
+
if (!value) {
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
const length = typeof value === "string" || Array.isArray(value) ? value.length : 0;
|
|
176
|
+
if (this.min !== undefined && length < this.min) {
|
|
177
|
+
throw new BadRequestException(`Length must be at least ${this.min} characters`);
|
|
178
|
+
}
|
|
179
|
+
if (this.max !== undefined && length > this.max) {
|
|
180
|
+
throw new BadRequestException(`Length must not exceed ${this.max} characters`);
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Validate Range Pipe - Validates number is within range
|
|
187
|
+
* @example
|
|
188
|
+
* @Get()
|
|
189
|
+
* async getData(@Query('page', new ValidateRangePipe(1, 100)) page: number) {}
|
|
190
|
+
*/
|
|
191
|
+
export class ValidateRangePipe {
|
|
192
|
+
min;
|
|
193
|
+
max;
|
|
194
|
+
constructor(min, max) {
|
|
195
|
+
this.min = min;
|
|
196
|
+
this.max = max;
|
|
197
|
+
}
|
|
198
|
+
transform(value, metadata) {
|
|
199
|
+
const num = typeof value === "string" ? parseFloat(value) : value;
|
|
200
|
+
if (isNaN(num)) {
|
|
201
|
+
throw new BadRequestException("Value must be a number");
|
|
202
|
+
}
|
|
203
|
+
if (this.min !== undefined && num < this.min) {
|
|
204
|
+
throw new BadRequestException(`Value must be at least ${this.min}`);
|
|
205
|
+
}
|
|
206
|
+
if (this.max !== undefined && num > this.max) {
|
|
207
|
+
throw new BadRequestException(`Value must not exceed ${this.max}`);
|
|
208
|
+
}
|
|
209
|
+
return num;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Strip HTML Pipe - Removes HTML tags from string
|
|
214
|
+
* @example
|
|
215
|
+
* @Post()
|
|
216
|
+
* async create(@Body('comment', StripHTMLPipe) comment: string) {}
|
|
217
|
+
*/
|
|
218
|
+
export class StripHTMLPipe {
|
|
219
|
+
transform(value, metadata) {
|
|
220
|
+
if (typeof value !== "string") {
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
return value.replace(/<[^>]*>/g, "");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Slugify Pipe - Converts string to URL-friendly slug
|
|
228
|
+
* @example
|
|
229
|
+
* @Post()
|
|
230
|
+
* async create(@Body('title', SlugifyPipe) slug: string) {}
|
|
231
|
+
*/
|
|
232
|
+
export class SlugifyPipe {
|
|
233
|
+
transform(value, metadata) {
|
|
234
|
+
if (typeof value !== "string") {
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
return value
|
|
238
|
+
.toLowerCase()
|
|
239
|
+
.trim()
|
|
240
|
+
.replace(/[^\w\s-]/g, "")
|
|
241
|
+
.replace(/[\s_-]+/g, "-")
|
|
242
|
+
.replace(/^-+|-+$/g, "");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Parse Comma Separated Pipe - Converts comma-separated string to array
|
|
247
|
+
* @example
|
|
248
|
+
* @Get()
|
|
249
|
+
* async search(@Query('tags', ParseCommaSeparatedPipe) tags: string[]) {}
|
|
250
|
+
*/
|
|
251
|
+
export class ParseCommaSeparatedPipe {
|
|
252
|
+
trim;
|
|
253
|
+
constructor(trim = true) {
|
|
254
|
+
this.trim = trim;
|
|
255
|
+
}
|
|
256
|
+
transform(value, metadata) {
|
|
257
|
+
if (!value || typeof value !== "string") {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
const items = value.split(",");
|
|
261
|
+
return this.trim ? items.map((item) => item.trim()) : items;
|
|
262
|
+
}
|
|
263
|
+
}
|