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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +522 -0
  3. package/dist/database.d.ts +36 -0
  4. package/dist/database.d.ts.map +1 -0
  5. package/dist/database.js +162 -0
  6. package/dist/decorators/database.decorators.d.ts +55 -0
  7. package/dist/decorators/database.decorators.d.ts.map +1 -0
  8. package/dist/decorators/database.decorators.js +131 -0
  9. package/dist/decorators/exception.advanced.d.ts +160 -0
  10. package/dist/decorators/exception.advanced.d.ts.map +1 -0
  11. package/dist/decorators/exception.advanced.js +232 -0
  12. package/dist/decorators/exception.decorators.d.ts +121 -0
  13. package/dist/decorators/exception.decorators.d.ts.map +1 -0
  14. package/dist/decorators/exception.decorators.js +242 -0
  15. package/dist/decorators/guard.decorators.d.ts +43 -0
  16. package/dist/decorators/guard.decorators.d.ts.map +1 -0
  17. package/dist/decorators/guard.decorators.js +67 -0
  18. package/dist/decorators/http.decorators.d.ts +130 -0
  19. package/dist/decorators/http.decorators.d.ts.map +1 -0
  20. package/dist/decorators/http.decorators.js +209 -0
  21. package/dist/decorators/interceptor.advanced.d.ts +93 -0
  22. package/dist/decorators/interceptor.advanced.d.ts.map +1 -0
  23. package/dist/decorators/interceptor.advanced.js +228 -0
  24. package/dist/decorators/interceptor.decorators.d.ts +91 -0
  25. package/dist/decorators/interceptor.decorators.d.ts.map +1 -0
  26. package/dist/decorators/interceptor.decorators.js +163 -0
  27. package/dist/decorators/param.decorators.d.ts +144 -0
  28. package/dist/decorators/param.decorators.d.ts.map +1 -0
  29. package/dist/decorators/param.decorators.js +205 -0
  30. package/dist/decorators/pipe.advanced.d.ts +125 -0
  31. package/dist/decorators/pipe.advanced.d.ts.map +1 -0
  32. package/dist/decorators/pipe.advanced.js +263 -0
  33. package/dist/decorators/pipe.decorators.d.ts +226 -0
  34. package/dist/decorators/pipe.decorators.d.ts.map +1 -0
  35. package/dist/decorators/pipe.decorators.js +420 -0
  36. package/dist/dto.d.ts +83 -0
  37. package/dist/dto.d.ts.map +1 -0
  38. package/dist/dto.js +88 -0
  39. package/dist/factory.d.ts +76 -0
  40. package/dist/factory.d.ts.map +1 -0
  41. package/dist/factory.js +410 -0
  42. package/dist/index.d.ts +28 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +43 -0
  45. package/dist/pipes/validation.pipe.d.ts +91 -0
  46. package/dist/pipes/validation.pipe.d.ts.map +1 -0
  47. package/dist/pipes/validation.pipe.js +163 -0
  48. 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
+ }