quantum-flow 1.0.1

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 (102) hide show
  1. package/.prettierrc.json +9 -0
  2. package/README.md +37 -0
  3. package/dist/app/aws/index.d.ts +1 -0
  4. package/dist/app/aws/index.js +17 -0
  5. package/dist/app/aws/lambda.d.ts +13 -0
  6. package/dist/app/aws/lambda.js +241 -0
  7. package/dist/app/http/Application.d.ts +23 -0
  8. package/dist/app/http/Application.js +208 -0
  9. package/dist/app/http/Socket.d.ts +16 -0
  10. package/dist/app/http/Socket.js +32 -0
  11. package/dist/app/http/decorators.d.ts +7 -0
  12. package/dist/app/http/decorators.js +81 -0
  13. package/dist/app/http/index.d.ts +3 -0
  14. package/dist/app/http/index.js +19 -0
  15. package/dist/app/http/websocket/WebsocetService.d.ts +20 -0
  16. package/dist/app/http/websocket/WebsocetService.js +41 -0
  17. package/dist/app/http/websocket/WebsocketServer.d.ts +30 -0
  18. package/dist/app/http/websocket/WebsocketServer.js +221 -0
  19. package/dist/constants.d.ts +16 -0
  20. package/dist/constants.js +24 -0
  21. package/dist/core/Controller.d.ts +43 -0
  22. package/dist/core/Controller.js +159 -0
  23. package/dist/core/Endpoint.d.ts +8 -0
  24. package/dist/core/Endpoint.js +43 -0
  25. package/dist/core/index.d.ts +4 -0
  26. package/dist/core/index.js +19 -0
  27. package/dist/core/utils/extractors.d.ts +15 -0
  28. package/dist/core/utils/extractors.js +29 -0
  29. package/dist/core/utils/helpers.d.ts +4 -0
  30. package/dist/core/utils/helpers.js +22 -0
  31. package/dist/core/utils/index.d.ts +3 -0
  32. package/dist/core/utils/index.js +19 -0
  33. package/dist/core/utils/websocket.d.ts +8 -0
  34. package/dist/core/utils/websocket.js +45 -0
  35. package/dist/types/common.d.ts +47 -0
  36. package/dist/types/common.js +2 -0
  37. package/dist/types/controller.d.ts +4 -0
  38. package/dist/types/controller.js +2 -0
  39. package/dist/types/http.d.ts +18 -0
  40. package/dist/types/http.js +2 -0
  41. package/dist/types/index.d.ts +5 -0
  42. package/dist/types/index.js +21 -0
  43. package/dist/types/lambda.d.ts +26 -0
  44. package/dist/types/lambda.js +2 -0
  45. package/dist/types/websocket.d.ts +55 -0
  46. package/dist/types/websocket.js +2 -0
  47. package/dist/utils/controller.d.ts +9 -0
  48. package/dist/utils/controller.js +111 -0
  49. package/dist/utils/helper.d.ts +1 -0
  50. package/dist/utils/helper.js +24 -0
  51. package/dist/utils/index.d.ts +7 -0
  52. package/dist/utils/index.js +23 -0
  53. package/dist/utils/multipart.d.ts +15 -0
  54. package/dist/utils/multipart.js +109 -0
  55. package/dist/utils/parsers.d.ts +4 -0
  56. package/dist/utils/parsers.js +76 -0
  57. package/dist/utils/server.d.ts +4 -0
  58. package/dist/utils/server.js +46 -0
  59. package/dist/utils/transform.d.ts +1 -0
  60. package/dist/utils/transform.js +23 -0
  61. package/dist/utils/validate.d.ts +1 -0
  62. package/dist/utils/validate.js +46 -0
  63. package/dist/validators/Validate.d.ts +3 -0
  64. package/dist/validators/Validate.js +40 -0
  65. package/dist/validators/index.d.ts +1 -0
  66. package/dist/validators/index.js +17 -0
  67. package/eslint.config.mjs +84 -0
  68. package/nodemon.json +5 -0
  69. package/package.json +70 -0
  70. package/src/app/aws/index.ts +1 -0
  71. package/src/app/aws/lambda.ts +283 -0
  72. package/src/app/http/Application.ts +250 -0
  73. package/src/app/http/Socket.ts +38 -0
  74. package/src/app/http/decorators.ts +115 -0
  75. package/src/app/http/index.ts +3 -0
  76. package/src/app/http/websocket/WebsocetService.ts +44 -0
  77. package/src/app/http/websocket/WebsocketServer.ts +262 -0
  78. package/src/constants.ts +25 -0
  79. package/src/core/Controller.ts +229 -0
  80. package/src/core/Endpoint.ts +39 -0
  81. package/src/core/index.ts +14 -0
  82. package/src/core/utils/extractors.ts +32 -0
  83. package/src/core/utils/helpers.ts +22 -0
  84. package/src/core/utils/index.ts +3 -0
  85. package/src/core/utils/websocket.ts +45 -0
  86. package/src/types/common.ts +60 -0
  87. package/src/types/controller.ts +2 -0
  88. package/src/types/http.ts +19 -0
  89. package/src/types/index.ts +5 -0
  90. package/src/types/lambda.ts +28 -0
  91. package/src/types/websocket.ts +57 -0
  92. package/src/utils/controller.ts +143 -0
  93. package/src/utils/helper.ts +24 -0
  94. package/src/utils/index.ts +7 -0
  95. package/src/utils/multipart.ts +93 -0
  96. package/src/utils/parsers.ts +87 -0
  97. package/src/utils/server.ts +49 -0
  98. package/src/utils/transform.ts +24 -0
  99. package/src/utils/validate.ts +53 -0
  100. package/src/validators/Validate.ts +48 -0
  101. package/src/validators/index.ts +1 -0
  102. package/tsconfig.json +51 -0
@@ -0,0 +1,45 @@
1
+ import { WS_METADATA_KEY, WS_SERVICE_KEY, WS_TOPIC_KEY } from '@constants';
2
+
3
+ import { WebSocketHandlerType } from '@types';
4
+
5
+ export function OnWS(type: WebSocketHandlerType, topic?: string) {
6
+ return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
7
+ const handlers = Reflect.getMetadata(WS_METADATA_KEY, target.constructor) || [];
8
+ handlers.push({ type, topic, method: propertyKey });
9
+ Reflect.defineMetadata(WS_METADATA_KEY, handlers, target.constructor);
10
+ return descriptor;
11
+ };
12
+ }
13
+
14
+ export function OnConnection() {
15
+ return OnWS('connection');
16
+ }
17
+
18
+ export function OnMessage(topic?: string) {
19
+ return OnWS('message', topic);
20
+ }
21
+
22
+ export function OnClose() {
23
+ return OnWS('close');
24
+ }
25
+
26
+ export function OnError() {
27
+ return OnWS('error');
28
+ }
29
+
30
+ export function Subscribe(topic: string) {
31
+ return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
32
+ const topics = Reflect.getMetadata(WS_TOPIC_KEY, target.constructor) || [];
33
+ topics.push({ topic, method: propertyKey });
34
+ Reflect.defineMetadata(WS_TOPIC_KEY, topics, target.constructor);
35
+ return descriptor;
36
+ };
37
+ }
38
+
39
+ export function InjectWS() {
40
+ return function (target: any, propertyKey: string, parameterIndex: number) {
41
+ const existingParams = Reflect.getMetadata(WS_SERVICE_KEY, target, propertyKey) || [];
42
+ existingParams.push({ index: parameterIndex });
43
+ Reflect.defineMetadata(WS_SERVICE_KEY, existingParams, target, propertyKey);
44
+ };
45
+ }
@@ -0,0 +1,60 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { IncomingHttpHeaders, ServerResponse } from 'http';
3
+
4
+ type P_Q = Record<string, string | undefined> | null | unknown;
5
+
6
+ export type Request<B = unknown, Q extends P_Q = unknown, P extends P_Q = unknown> = {
7
+ method: string;
8
+ url: URL;
9
+ headers: IncomingHttpHeaders;
10
+ query?: Q;
11
+ params?: P;
12
+ body: B;
13
+ isBase64Encoded?: boolean;
14
+ };
15
+ export type Router = (
16
+ req: Request,
17
+ res?: ServerResponse,
18
+ ) => Promise<{ status: number; data: any; message?: string }>;
19
+
20
+ export type EndpointResponse<T = any> = {
21
+ status: number;
22
+ data?: T;
23
+ error?: any;
24
+ };
25
+
26
+ export type AxiosQuery = {
27
+ data?: { [key: string]: any };
28
+ headers?: { [key: string]: any };
29
+ params?: { [key: string]: any };
30
+ url: string;
31
+ method: 'POST' | 'GET' | 'PATCH' | 'DELETE';
32
+ };
33
+
34
+ export interface IController {
35
+ handleRequest: Router;
36
+ }
37
+
38
+ export type Middleware = (req: Request, res?: ServerResponse) => Promise<Request> | Request;
39
+ export type Interceptor = (
40
+ data: any,
41
+ req?: Request,
42
+ res?: ServerResponse,
43
+ ) => Promise<unknown> | unknown;
44
+
45
+ export type ParamDecoratorType =
46
+ | 'body'
47
+ | 'params'
48
+ | 'query'
49
+ | 'request'
50
+ | 'headers'
51
+ | 'cookies'
52
+ | 'response'
53
+ | 'multipart';
54
+
55
+ export interface ParamMetadata {
56
+ index: number;
57
+ type: ParamDecoratorType;
58
+ dto?: any;
59
+ name?: string;
60
+ }
@@ -0,0 +1,2 @@
1
+ export type ControllerClass = { new (...args: any[]): any };
2
+ export type ControllerInstance = InstanceType<ControllerClass>;
@@ -0,0 +1,19 @@
1
+ export interface ServerConfig {
2
+ port?: number;
3
+ host?: string;
4
+ globalMiddlewares?: any[];
5
+ globalInterceptors?: any[];
6
+ globalErrorHandler?: any;
7
+ controllers?: any[];
8
+ websocket?: {
9
+ enabled: boolean;
10
+ path?: string;
11
+ lazy?: boolean;
12
+ };
13
+ }
14
+
15
+ export type Conf = ServerConfig & {
16
+ globalMiddlewares?: any[];
17
+ globalInterceptors?: any[];
18
+ globalErrorHandler?: any;
19
+ };
@@ -0,0 +1,5 @@
1
+ export * from './common';
2
+ export * from './controller';
3
+ export * from './http';
4
+ export * from './lambda';
5
+ export * from './websocket';
@@ -0,0 +1,28 @@
1
+ import { APIGatewayProxyEvent, Context } from 'aws-lambda';
2
+
3
+ export interface LambdaRequest {
4
+ method: string;
5
+ path: string;
6
+ headers: Record<string, string | string[]>;
7
+ query: Record<string, string | string[]>;
8
+ body: any;
9
+ params: Record<string, string>;
10
+ cookies: Record<string, string>;
11
+ raw: APIGatewayProxyEvent;
12
+ context: Context;
13
+ isBase64Encoded: boolean;
14
+ requestId: string;
15
+ stage: string;
16
+ sourceIp: string;
17
+ userAgent: string;
18
+ url: URL;
19
+ }
20
+
21
+ export interface LambdaResponse {
22
+ statusCode: number;
23
+ headers?: Record<string, string>;
24
+ body: string;
25
+ isBase64Encoded?: boolean;
26
+ multiValueHeaders?: Record<string, string[]>;
27
+ cookies?: string[];
28
+ }
@@ -0,0 +1,57 @@
1
+ import WebSocket from 'ws';
2
+
3
+ export type WebSocketHandlerType = 'connection' | 'message' | 'close' | 'error';
4
+
5
+ export interface WebSocketMessage {
6
+ type: string;
7
+ topic?: string;
8
+ data: any;
9
+ clientId?: string;
10
+ }
11
+
12
+ export interface WebSocketEvent {
13
+ type: WebSocketHandlerType;
14
+ client: WebSocketClient;
15
+ message?: WebSocketMessage;
16
+ data?: any;
17
+ }
18
+
19
+ export interface WebSocketClient {
20
+ id: string;
21
+ socket: WebSocket;
22
+ topics: Set<string>;
23
+ data: Record<string, any>;
24
+ connectedAt: Date;
25
+ }
26
+
27
+ export interface WebSocketMessage {
28
+ type: string;
29
+ topic?: string;
30
+ data: any;
31
+ clientId?: string;
32
+ }
33
+
34
+ export interface WebSocketStats {
35
+ clients: number;
36
+ topics: Array<{
37
+ topic: string;
38
+ subscribers: number;
39
+ }>;
40
+ }
41
+
42
+ export interface IWebSocketService {
43
+ sendToClient(clientId: string, message: any): boolean;
44
+ publishToTopic(topic: string, data: any, exclude?: string[]): void;
45
+ broadcast(message: any, excludeClientId?: string): void;
46
+ getStats(): { clients: number; topics: Array<{ topic: string; subscribers: number }> };
47
+ isAvailable(): boolean;
48
+ }
49
+
50
+ export interface IWebSocketServer {
51
+ sendToClient(clientId: string, message: any): boolean;
52
+ publishToTopic(topic: string, data: any, exclude: string[]): void;
53
+ broadcast(message: any, excludeClientId?: string): void;
54
+ getStats(): WebSocketStats;
55
+ subscribeToTopic(client: WebSocketClient, topic: string): void;
56
+ unsubscribeFromTopic(client: WebSocketClient, topic: string): void;
57
+ }
@@ -0,0 +1,143 @@
1
+ import { ControllerInstance, Middleware, ParamMetadata } from '@types';
2
+ import { MultipartProcessor } from '@utils';
3
+ import { WebSocketService } from '../app/http/websocket/WebsocetService';
4
+ import { validate } from './validate';
5
+
6
+ import { ENDPOINT, MIDDLEWARES, PARAM_METADATA_KEY, TO_VALIDATE, WS_SERVICE_KEY } from '@constants';
7
+ import { ServerResponse } from 'http';
8
+
9
+ const getBodyAndMultipart = (payload: any) => {
10
+ let body = payload.body;
11
+ let multipart;
12
+ if (MultipartProcessor.isMultipart({ headers: payload.headers })) {
13
+ try {
14
+ const { fields, files } = MultipartProcessor.parse({
15
+ body: payload.rawBody || payload.body,
16
+ headers: payload.headers,
17
+ isBase64Encoded: payload.isBase64Encoded,
18
+ });
19
+ multipart = files;
20
+ body = fields;
21
+ } catch (error) {
22
+ console.error('❌ Multipart parsing error:', error);
23
+ throw { status: 400, message: 'Invalid multipart data' };
24
+ }
25
+ }
26
+
27
+ return { multipart, body };
28
+ };
29
+
30
+ export const executeControllerMethod = async (
31
+ controller: ControllerInstance,
32
+ propertyName: string,
33
+ payload: any,
34
+ response?: ServerResponse,
35
+ ) => {
36
+ const fn = controller[propertyName];
37
+ if (typeof fn !== 'function') return null;
38
+ const endpointMeta = Reflect.getMetadata(ENDPOINT, controller, propertyName);
39
+ if (!endpointMeta) return null;
40
+
41
+ const methodMiddlewares: Middleware[] =
42
+ Reflect.getMetadata(MIDDLEWARES, controller, propertyName) || [];
43
+
44
+ for (let i = 0; i < methodMiddlewares.length; i++) {
45
+ const middleware = methodMiddlewares[i];
46
+ const result = await middleware(payload);
47
+ if (result) {
48
+ payload = { ...payload, ...result };
49
+ }
50
+ }
51
+
52
+ const prototype = Object.getPrototypeOf(controller);
53
+ const paramMetadata: ParamMetadata[] =
54
+ Reflect.getMetadata(PARAM_METADATA_KEY, prototype, propertyName) || [];
55
+
56
+ const { body, multipart } = getBodyAndMultipart(payload);
57
+ if (paramMetadata.length === 0) {
58
+ return fn.call(controller, payload);
59
+ }
60
+
61
+ const args: any[] = [];
62
+
63
+ const wsParams = Reflect.getMetadata(WS_SERVICE_KEY, controller, propertyName) || [];
64
+ const totalParams = Math.max(
65
+ paramMetadata.length ? Math.max(...paramMetadata.map((p) => p.index)) + 1 : 0,
66
+ wsParams.length ? Math.max(...wsParams.map((p: any) => p.index)) + 1 : 0,
67
+ );
68
+
69
+ for (let i = 0; i < totalParams; i++) {
70
+ const wsParam = wsParams.find((p: any) => p.index === i);
71
+ if (wsParam) {
72
+ args[i] = WebSocketService.getInstance();
73
+ continue;
74
+ }
75
+
76
+ const param = paramMetadata.find((p) => p.index === i);
77
+ if (!param) {
78
+ args[i] = undefined;
79
+ continue;
80
+ }
81
+
82
+ let value = param.name ? payload[param.type]?.[param.name] : payload[param.type];
83
+
84
+ if (param.type === 'multipart') {
85
+ value = multipart;
86
+ }
87
+ if (param.type === 'request') {
88
+ value = payload;
89
+ }
90
+ if (param.type === 'response') {
91
+ value = response;
92
+ }
93
+ if (param.type === 'response') {
94
+ value = response;
95
+ }
96
+ if (param.type === 'body') {
97
+ value = body;
98
+ }
99
+
100
+ if (TO_VALIDATE.includes(param.type)) {
101
+ value = await validate(param.dto, value);
102
+ }
103
+
104
+ args[i] = value;
105
+ }
106
+
107
+ return fn.apply(controller, args);
108
+ };
109
+
110
+ export const getControllerMethods = (controller: ControllerInstance) => {
111
+ const methods: Array<{
112
+ name: string;
113
+ httpMethod: string;
114
+ pattern: string;
115
+ middlewares?: Array<(req: any, res?: ServerResponse) => any>;
116
+ }> = [];
117
+
118
+ let proto = Object.getPrototypeOf(controller);
119
+
120
+ while (proto && proto !== Object.prototype) {
121
+ const propertyNames = Object.getOwnPropertyNames(proto);
122
+ for (const propertyName of propertyNames) {
123
+ if (propertyName === 'constructor') continue;
124
+
125
+ const endpointMeta = Reflect.getMetadata(ENDPOINT, proto, propertyName);
126
+
127
+ if (endpointMeta) {
128
+ const [httpMethod, pattern] = endpointMeta;
129
+ const methodMiddlewares = Reflect.getMetadata(MIDDLEWARES, proto, propertyName);
130
+
131
+ methods.push({
132
+ name: propertyName,
133
+ httpMethod,
134
+ pattern,
135
+ middlewares: methodMiddlewares,
136
+ });
137
+ }
138
+ }
139
+ proto = Object.getPrototypeOf(proto);
140
+ }
141
+
142
+ return methods;
143
+ };
@@ -0,0 +1,24 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ export const matchRoute = (routePattern: string, actualPath: string) => {
4
+ const routeParts = routePattern.split('/').filter(Boolean);
5
+ const pathParts = actualPath.split('/').filter(Boolean);
6
+
7
+ if (routeParts.length !== pathParts.length) return null;
8
+
9
+ const params: Record<string, string> = {};
10
+
11
+ for (let i = 0; i < routeParts.length; i++) {
12
+ const routePart = routeParts[i];
13
+ const pathPart = pathParts[i];
14
+
15
+ if (routePart.startsWith(':')) {
16
+ const paramName = routePart.slice(1);
17
+ params[paramName] = decodeURIComponent(pathPart);
18
+ } else if (routePart !== pathPart) {
19
+ return null;
20
+ }
21
+ }
22
+
23
+ return params;
24
+ };
@@ -0,0 +1,7 @@
1
+ export * from './controller';
2
+ export * from './helper';
3
+ export * from './multipart';
4
+ export * from './parsers';
5
+ export * from './server';
6
+ export * from './transform';
7
+ export * from './validate';
@@ -0,0 +1,93 @@
1
+ // utils/MultipartProcessor.ts
2
+ import * as multipart from 'parse-multipart-data';
3
+
4
+ export interface MultipartFile {
5
+ fieldname: string;
6
+ filename: string;
7
+ contentType: string;
8
+ data: Buffer;
9
+ size: number;
10
+ encoding?: string;
11
+ }
12
+
13
+ export class MultipartProcessor {
14
+ static parse(request: any): {
15
+ fields: Record<string, any>;
16
+ files: Record<string, MultipartFile | MultipartFile[]>;
17
+ } {
18
+ const { body, headers, isBase64Encoded } = request;
19
+
20
+ if (!body) {
21
+ return { fields: {}, files: {} };
22
+ }
23
+
24
+ let contentType = headers['content-type'] ?? headers['Content-Type'] ?? '';
25
+ if (Array.isArray(contentType)) {
26
+ contentType = contentType[0];
27
+ }
28
+
29
+ if (!contentType.startsWith('multipart/form-data')) {
30
+ throw new Error('Not a multipart request');
31
+ }
32
+
33
+ const boundaryMatch = multipart.getBoundary(contentType);
34
+ if (!boundaryMatch) {
35
+ throw new Error('Invalid multipart boundary');
36
+ }
37
+
38
+ let bodyBuffer: Buffer;
39
+ if (Buffer.isBuffer(body)) {
40
+ bodyBuffer = body;
41
+ } else if (typeof body === 'string') {
42
+ bodyBuffer = isBase64Encoded ? Buffer.from(body, 'base64') : Buffer.from(body, 'binary');
43
+ } else {
44
+ bodyBuffer = Buffer.from(JSON.stringify(body));
45
+ }
46
+
47
+ const parts = multipart.parse(bodyBuffer, boundaryMatch);
48
+
49
+ const fields: Record<string, any> = {};
50
+ const files: Record<string, MultipartFile | MultipartFile[]> = {};
51
+
52
+ parts.forEach((part: any) => {
53
+ if (part.filename) {
54
+ const fieldName = part.name || 'file';
55
+
56
+ const fileData: MultipartFile = {
57
+ fieldname: fieldName,
58
+ filename: part.filename,
59
+ contentType: part.type,
60
+ data: part.data,
61
+ size: part.data.length,
62
+ encoding: part.encoding,
63
+ };
64
+
65
+ if (files[fieldName]) {
66
+ if (Array.isArray(files[fieldName])) {
67
+ (files[fieldName] as MultipartFile[]).push(fileData);
68
+ } else {
69
+ files[fieldName] = [files[fieldName] as MultipartFile, fileData];
70
+ }
71
+ } else {
72
+ files[fieldName] = fileData;
73
+ }
74
+ } else if (part.name) {
75
+ const text = part.data.toString('utf-8').trim();
76
+
77
+ try {
78
+ fields[part.name] = JSON.parse(text);
79
+ } catch {
80
+ fields[part.name] = text;
81
+ }
82
+ }
83
+ });
84
+
85
+ return { fields, files };
86
+ }
87
+
88
+ static isMultipart(request: any): boolean {
89
+ const contentType =
90
+ request.headers?.['content-type'] || request.headers?.['Content-Type'] || '';
91
+ return contentType.startsWith('multipart/form-data');
92
+ }
93
+ }
@@ -0,0 +1,87 @@
1
+ import http from 'http';
2
+ export const ParseQuery = (url: URL) => {
3
+ const params = url.searchParams;
4
+ const query: Record<string, string | string[]> = {};
5
+
6
+ for (const key of params.keys()) {
7
+ const values = params.getAll(key);
8
+
9
+ query[key] = values.length > 1 ? values : values[0];
10
+ }
11
+
12
+ return query;
13
+ };
14
+
15
+ export const ParseBody = (request: any): any => {
16
+ if (request.body && typeof request.body === 'object' && !Buffer.isBuffer(request.body)) {
17
+ return request.body;
18
+ }
19
+
20
+ const { body, headers, isBase64Encoded } = request;
21
+
22
+ if (!body) {
23
+ return {};
24
+ }
25
+
26
+ let contentType = headers['content-type'] ?? headers['Content-Type'] ?? '';
27
+ if (Array.isArray(contentType)) {
28
+ contentType = contentType[0];
29
+ }
30
+
31
+ const cleanContentType = contentType.split(';')[0].trim().toLowerCase();
32
+
33
+ if (cleanContentType === 'application/json') {
34
+ try {
35
+ if (typeof body === 'string') {
36
+ return JSON.parse(body);
37
+ }
38
+ if (Buffer.isBuffer(body)) {
39
+ return JSON.parse(body.toString('utf8'));
40
+ }
41
+ } catch (error) {
42
+ console.error(error);
43
+ throw error;
44
+ }
45
+ }
46
+
47
+ if (cleanContentType.startsWith('text/')) {
48
+ if (Buffer.isBuffer(body)) {
49
+ return { text: body.toString('utf8') };
50
+ }
51
+ return { text: body };
52
+ }
53
+
54
+ if (cleanContentType === 'application/x-www-form-urlencoded') {
55
+ if (Buffer.isBuffer(body)) {
56
+ const text = body.toString('utf8');
57
+ const params = new URLSearchParams(text);
58
+ const result: Record<string, any> = {};
59
+ params.forEach((value, key) => {
60
+ result[key] = value;
61
+ });
62
+ return result;
63
+ }
64
+ }
65
+
66
+ if (Buffer.isBuffer(body)) {
67
+ return { raw: body.toString('utf8') };
68
+ }
69
+
70
+ return body;
71
+ };
72
+
73
+ export const ParseCookies = (req: http.IncomingMessage): Record<string, string> => {
74
+ const cookieHeader = req.headers.cookie;
75
+ if (!cookieHeader) return {};
76
+
77
+ return cookieHeader.split(';').reduce(
78
+ (cookies, cookie) => {
79
+ const [name, value] = cookie.trim().split('=');
80
+ if (name && value) {
81
+ cookies[name] = decodeURIComponent(value);
82
+ }
83
+ return cookies;
84
+ },
85
+ {} as Record<string, string>,
86
+ );
87
+ };
@@ -0,0 +1,49 @@
1
+ import { SERVER_CONFIG_KEY, SERVER_MODULES_KEY } from '@constants';
2
+ import { ServerConfig } from '@types';
3
+ import http from 'http';
4
+
5
+ export const resolveConfig = (configOrClass?: any): ServerConfig => {
6
+ let config: ServerConfig = {};
7
+
8
+ if (configOrClass && typeof configOrClass === 'function') {
9
+ const decoratorConfig = Reflect.getMetadata(SERVER_CONFIG_KEY, configOrClass) || {};
10
+ const controllers = Reflect.getMetadata(SERVER_MODULES_KEY, configOrClass) || [];
11
+
12
+ config = {
13
+ port: 3000,
14
+ host: 'localhost',
15
+ ...decoratorConfig,
16
+ controllers: [...controllers, ...(decoratorConfig.controllers || [])],
17
+ };
18
+ } else if (configOrClass && typeof configOrClass === 'object') {
19
+ config = { port: 3000, host: 'localhost', ...configOrClass };
20
+ } else {
21
+ config = {
22
+ port: 3000,
23
+ host: 'localhost',
24
+ controllers: [],
25
+ };
26
+ console.log('⚙️ Using default configuration');
27
+ }
28
+
29
+ return config;
30
+ };
31
+
32
+ export const collectRawBody = (req: http.IncomingMessage): Promise<Buffer> => {
33
+ return new Promise((resolve) => {
34
+ const chunks: Buffer[] = [];
35
+
36
+ req.on('data', (chunk) => {
37
+ chunks.push(Buffer.from(chunk));
38
+ });
39
+
40
+ req.on('end', () => {
41
+ const buffer = Buffer.concat(chunks);
42
+ resolve(buffer);
43
+ });
44
+
45
+ req.on('error', () => {
46
+ resolve(Buffer.from(''));
47
+ });
48
+ });
49
+ };
@@ -0,0 +1,24 @@
1
+ export async function transformAndValidate(dtoClass: any, data: any) {
2
+ if (!dtoClass) return data;
3
+
4
+ if (typeof dtoClass.from === 'function') {
5
+ return dtoClass.from(data);
6
+ }
7
+ if (typeof dtoClass === 'function') {
8
+ const instance = new dtoClass();
9
+ Object.entries(data).forEach(([key, val]) => {
10
+ instance[key] = val;
11
+ });
12
+
13
+ Object.assign(instance, data);
14
+ if (typeof instance.validate === 'function') {
15
+ await instance.validate();
16
+ }
17
+
18
+ console.log('ppppppp', dtoClass, data);
19
+
20
+ return instance;
21
+ }
22
+
23
+ return data;
24
+ }