quantum-flow 1.3.9 → 1.4.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 (49) hide show
  1. package/README.md +6 -8
  2. package/dist/app/aws/helpers.d.ts +25 -0
  3. package/dist/app/aws/helpers.js +46 -0
  4. package/dist/app/aws/lambda.d.ts +7 -10
  5. package/dist/app/aws/lambda.js +177 -186
  6. package/dist/app/aws/utils/response.d.ts +7 -0
  7. package/dist/app/aws/utils/response.js +13 -0
  8. package/dist/app/http/Application.js +10 -0
  9. package/dist/app/http/decorators.js +1 -0
  10. package/dist/constants.d.ts +1 -0
  11. package/dist/constants.js +2 -1
  12. package/dist/core/Controller.d.ts +1 -9
  13. package/dist/core/Controller.js +23 -57
  14. package/dist/core/index.d.ts +1 -1
  15. package/dist/core/utils/cors.d.ts +2 -0
  16. package/dist/core/utils/cors.js +21 -0
  17. package/dist/core/utils/index.d.ts +1 -0
  18. package/dist/core/utils/index.js +1 -0
  19. package/dist/examples/app.d.ts +5 -0
  20. package/dist/examples/app.js +40 -0
  21. package/dist/examples/controllers/user.js +2 -1
  22. package/dist/examples/controllers/userMetadata.d.ts +4 -0
  23. package/dist/examples/controllers/userMetadata.js +20 -1
  24. package/dist/examples/lambda.d.ts +1 -0
  25. package/dist/examples/lambda.js +6 -0
  26. package/dist/examples/mock/context.d.ts +14 -0
  27. package/dist/examples/mock/context.js +17 -0
  28. package/dist/examples/server.d.ts +1 -1
  29. package/dist/examples/server.js +2 -29
  30. package/dist/types/common.d.ts +7 -13
  31. package/dist/types/controller.d.ts +3 -0
  32. package/dist/types/cors.d.ts +10 -0
  33. package/dist/types/cors.js +2 -0
  34. package/dist/types/http.d.ts +2 -0
  35. package/dist/types/index.d.ts +1 -0
  36. package/dist/types/index.js +1 -0
  37. package/dist/types/lambda.d.ts +48 -2
  38. package/dist/utils/controller.d.ts +9 -1
  39. package/dist/utils/controller.js +62 -5
  40. package/dist/utils/cors.d.ts +8 -0
  41. package/dist/utils/cors.js +126 -0
  42. package/dist/utils/index.d.ts +3 -1
  43. package/dist/utils/index.js +3 -1
  44. package/dist/utils/lambda.d.ts +2 -0
  45. package/dist/utils/lambda.js +53 -0
  46. package/dist/utils/multipart.d.ts +1 -0
  47. package/dist/utils/multipart.js +98 -1
  48. package/dist/utils/server.js +1 -0
  49. package/package.json +1 -1
package/README.md CHANGED
@@ -41,7 +41,8 @@ import {
41
41
  Request,
42
42
  Response,
43
43
  Status,
44
- USE
44
+ USE,
45
+ CORS
45
46
  } from 'quantum-flow/core';
46
47
  import {IsString} from 'class-validator'
47
48
 
@@ -58,10 +59,12 @@ class UserDto {
58
59
  return { data, intercepted: true };
59
60
  },
60
61
  })
62
+ @CORS({ origin: '*' })
61
63
  @Catch((err) => ({ status: 500, err }))
62
64
  export class User {
63
65
  @Status(201)
64
66
  @PUT(':id',[...middlewares])
67
+ @CORS({ origin: '*' })
65
68
  async createUser(
66
69
  @Body(UserDto) body: UserDto,
67
70
  @Query() query: any,
@@ -97,7 +100,7 @@ Use the `@Server` decorator with configuration options like port, host, controll
97
100
  ```typescript
98
101
  import { Server, Port, Host, Use, Catch, HttpServer } from 'quantum-flow/http';
99
102
 
100
- @Server({ controllers: [RootController] })
103
+ @Server({ controllers: [RootController], cors: { origin: '*' } })
101
104
  @Port(3000)
102
105
  @Host('localhost')
103
106
  @Use((data) => data)
@@ -115,6 +118,7 @@ server.listen().catch(console.error);
115
118
  - Use `@Use` to apply middlewares.
116
119
  - Use `@Catch` to handle errors.
117
120
  - Use `@Port` and `@Host` to configure server port and host.
121
+ - Use `@CORS` to configure cors.
118
122
 
119
123
  ## Request decorators
120
124
 
@@ -160,8 +164,6 @@ Enable WebSocket in the server configuration and register WebSocket controllers.
160
164
  export class Socket {
161
165
  @OnConnection()
162
166
  onConnection(event: WebSocketEvent) {
163
- console.log(`✅ Connected: ${event.client.id}`);
164
-
165
167
  // Send greeting ONLY to this client
166
168
  event.client.socket.send(
167
169
  JSON.stringify({
@@ -181,8 +183,6 @@ export class Socket {
181
183
  // The message is ALREADY automatically broadcast to all!
182
184
 
183
185
  const msg = event.message?.data;
184
- console.log(`💬 Message in chat: ${msg?.text}`);
185
-
186
186
  // You can add logic, but no need to broadcast
187
187
  if (msg?.text.includes('bad')) {
188
188
  // If return empty, the message will not be sent
@@ -197,7 +197,6 @@ export class Socket {
197
197
  */
198
198
  @Subscribe('news')
199
199
  onNewsMessage(event: WebSocketEvent) {
200
- console.log(`📰 News: ${event.message?.data.title}`);
201
200
  // Automatic broadcast to all subscribed to 'news'
202
201
  }
203
202
 
@@ -221,7 +220,6 @@ export class Socket {
221
220
  @OnMessage('subscribe')
222
221
  onSubscribe(event: WebSocketEvent) {
223
222
  const topic = event.message?.data.topic;
224
- console.log(`📌 Client ${event.client.id} subscribed to ${topic}`);
225
223
 
226
224
  // Server will save the subscription automatically, no need to do anything!
227
225
  // Just confirm
@@ -0,0 +1,25 @@
1
+ import { AppRequest, HTTP_METHODS } from '../../types/index.js';
2
+ import { IncomingHttpHeaders } from 'http';
3
+ export declare class LResponse {
4
+ headers: IncomingHttpHeaders;
5
+ constructor();
6
+ setHeader(header: string, value: string): void;
7
+ }
8
+ export declare class LRequest {
9
+ headers: IncomingHttpHeaders;
10
+ url: URL;
11
+ method: HTTP_METHODS;
12
+ path?: string;
13
+ query?: any;
14
+ params?: any;
15
+ body: any;
16
+ cookies: Record<string, string>;
17
+ _startTime: number;
18
+ event?: any;
19
+ context?: any;
20
+ requestId?: string;
21
+ stage?: string;
22
+ sourceIp?: string;
23
+ userAgent?: string;
24
+ constructor(request: AppRequest);
25
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LRequest = exports.LResponse = void 0;
4
+ class LResponse {
5
+ headers = {};
6
+ constructor() { }
7
+ setHeader(header, value) {
8
+ this.headers[header] = value;
9
+ }
10
+ }
11
+ exports.LResponse = LResponse;
12
+ class LRequest {
13
+ headers;
14
+ url;
15
+ method;
16
+ path;
17
+ query;
18
+ params;
19
+ body;
20
+ cookies;
21
+ _startTime;
22
+ event;
23
+ context;
24
+ requestId;
25
+ stage;
26
+ sourceIp;
27
+ userAgent;
28
+ constructor(request) {
29
+ this.headers = { ...request.headers };
30
+ this.url = request.url;
31
+ this.method = request.method;
32
+ this.path = request.path;
33
+ this.query = request.query;
34
+ this.params = request.params;
35
+ this.body = request.body;
36
+ this.cookies = request.cookies;
37
+ this._startTime = request._startTime;
38
+ this.event = request.event;
39
+ this.context = request.context;
40
+ this.requestId = request.requestId;
41
+ this.stage = request.stage;
42
+ this.sourceIp = request.sourceIp;
43
+ this.userAgent = request.userAgent;
44
+ }
45
+ }
46
+ exports.LRequest = LRequest;
@@ -1,13 +1,10 @@
1
- import { APIGatewayProxyEvent, APIGatewayProxyResult, Context, Handler } from 'aws-lambda';
2
- import { LambdaApp, LambdaRequest, LambdaResponse } from '../../types/index.js';
1
+ import { LambdaApp } from '../../types/index.js';
2
+ import { Handler } from 'aws-lambda';
3
3
  export declare class LambdaAdapter {
4
- private static getHeaderValue;
5
- static toRequest(event: APIGatewayProxyEvent, context: Context): LambdaRequest;
6
- static createResponseBody(response: any): any;
7
- static toLambdaResponse(response: any, request?: LambdaRequest): LambdaResponse;
8
- static createHandler(Contoller: new (...args: any[]) => LambdaApp): Handler<APIGatewayProxyEvent, APIGatewayProxyResult>;
9
- private static safeHeaders;
10
- private static safeParams;
4
+ static createHandler(Controller: new (...args: any[]) => LambdaApp): Handler;
5
+ private static getEventType;
6
+ private static toRequest;
7
+ private static toLambdaResponse;
8
+ private static handleError;
11
9
  private static getSourceIp;
12
- private static getUserAgent;
13
10
  }
@@ -3,242 +3,233 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LambdaAdapter = void 0;
4
4
  const _constants_1 = require("../../constants.js");
5
5
  const _utils_1 = require("../../utils/index.js");
6
+ const helpers_1 = require("./helpers");
6
7
  class LambdaAdapter {
7
- static getHeaderValue(headers, headerName) {
8
- const value = headers?.[headerName] || headers?.[headerName.toLowerCase()];
9
- if (!value)
10
- return undefined;
11
- if (Array.isArray(value)) {
12
- return value[0];
8
+ static createHandler(Controller) {
9
+ return async (event, context) => {
10
+ const instance = new Controller();
11
+ if (Object.hasOwn(instance, 'beforeStart')) {
12
+ await instance.beforeStart?.();
13
+ }
14
+ try {
15
+ const cors = Reflect.getMetadata(_constants_1.CORS_METADATA, instance);
16
+ const eventType = this.getEventType(event);
17
+ const normalizedEvent = (0, _utils_1.normalizeEvent)(event, eventType);
18
+ const request = this.toRequest(normalizedEvent, context);
19
+ const lambdaRequest = new helpers_1.LRequest(request);
20
+ const lambdaResponse = new helpers_1.LResponse();
21
+ let handledCors = { permitted: true, continue: true };
22
+ if (cors) {
23
+ handledCors = (0, _utils_1.handleCORS)(request, lambdaResponse, cors);
24
+ }
25
+ if (!handledCors.permitted) {
26
+ return this.toLambdaResponse({ status: 403, message: 'CORS: Origin not allowed' }, lambdaRequest, lambdaResponse, eventType);
27
+ }
28
+ if (!handledCors.continue && handledCors.permitted) {
29
+ return this.toLambdaResponse({ status: 204 }, lambdaRequest, lambdaResponse, eventType);
30
+ }
31
+ if (typeof instance.handleRequest !== 'function') {
32
+ throw new Error('Controller must have handleRequest method');
33
+ }
34
+ const response = await instance.handleRequest(request, lambdaRequest, lambdaResponse);
35
+ return this.toLambdaResponse(response, lambdaRequest, lambdaResponse, eventType);
36
+ }
37
+ catch (error) {
38
+ return this.handleError(error, event, context);
39
+ }
40
+ };
41
+ }
42
+ static getEventType(event) {
43
+ if (event.httpMethod && event.resource) {
44
+ return 'rest';
45
+ }
46
+ if (event.version === '2.0' || event.requestContext?.http) {
47
+ return 'http';
48
+ }
49
+ if (event.version && event.rawPath && !event.requestContext?.http) {
50
+ return 'url';
13
51
  }
14
- return value;
52
+ return 'rest';
15
53
  }
16
54
  static toRequest(event, context) {
17
55
  const query = {};
18
- if (event.queryStringParameters) {
56
+ if (event.multiValueQueryStringParameters) {
57
+ Object.entries(event.multiValueQueryStringParameters).forEach(([key, value]) => {
58
+ query[key] = value;
59
+ });
60
+ }
61
+ else {
19
62
  Object.entries(event.queryStringParameters).forEach(([key, value]) => {
20
- if (event.multiValueQueryStringParameters?.[key]) {
21
- query[key] = event.multiValueQueryStringParameters[key];
22
- }
23
- else {
24
- query[key] = value || '';
25
- }
63
+ query[key] = value;
26
64
  });
27
65
  }
28
66
  const cookies = {};
29
- const cookieHeader = event.headers?.Cookie || event.headers?.cookie;
67
+ const cookieHeader = event.headers?.Cookie || event.headers?.cookie || event.headers?.cookies;
30
68
  if (cookieHeader) {
31
- cookieHeader.split(';').forEach((cookie) => {
32
- const [name, value] = cookie.trim().split('=');
33
- if (name && value) {
34
- cookies[name] = decodeURIComponent(value);
35
- }
36
- });
37
- }
38
- let body = (0, _utils_1.ParseBody)(event);
39
- const protocol = event.headers?.['x-forwarded-proto'] || 'http';
40
- const host = event.headers?.host || 'localhost';
41
- const cleanPath = event.path.split('?')[0];
42
- const fullUrl = `${protocol}://${host}${cleanPath.startsWith('/') ? '' : '/'}${cleanPath}`;
43
- let url;
44
- try {
45
- url = new URL(fullUrl);
46
- if (event.queryStringParameters) {
47
- Object.entries(event.queryStringParameters).forEach(([key, value]) => {
48
- if (value)
49
- url.searchParams.append(key, value);
69
+ if (Array.isArray(cookieHeader)) {
70
+ cookieHeader.forEach((cookie) => {
71
+ const [name, value] = cookie.split('=');
72
+ if (name && value)
73
+ cookies[name] = decodeURIComponent(value);
74
+ });
75
+ }
76
+ else {
77
+ cookieHeader.split(';').forEach((cookie) => {
78
+ const [name, value] = cookie.trim().split('=');
79
+ if (name && value)
80
+ cookies[name] = decodeURIComponent(value);
50
81
  });
51
82
  }
52
83
  }
53
- catch (error) {
54
- console.error('❌ Failed to create URL:', fullUrl, error);
55
- url = new URL('http://localhost/');
84
+ let rawBody = Buffer.from(event.body ?? '', 'base64');
85
+ let body = event.body || {};
86
+ if (event.body && event.isBase64Encoded) {
87
+ body = rawBody.toString('utf-8');
56
88
  }
89
+ if (typeof body === 'string' && body.trim().startsWith('{')) {
90
+ try {
91
+ body = JSON.parse(body);
92
+ }
93
+ catch (e) { }
94
+ }
95
+ const xForvarded = Array.isArray(event.headers['x-forwarded-proto'])
96
+ ? event.headers['x-forwarded-proto']?.[0]
97
+ : event.headers['x-forwarded-proto'];
98
+ const xhost = Array.isArray(event.headers['host'])
99
+ ? event.headers['host']?.[0]
100
+ : event.headers['host'];
101
+ const protocol = xForvarded || 'https';
102
+ const host = xhost || 'localhost:3000';
103
+ const fullUrl = `${protocol}://${host}${event.path}`;
104
+ let url = new URL(fullUrl);
57
105
  return {
58
- method: event.httpMethod,
59
- path: cleanPath,
106
+ method: event.httpMethod.toUpperCase(),
60
107
  url,
61
- headers: this.safeHeaders(event.headers),
108
+ headers: event.headers,
62
109
  query,
63
110
  body,
64
- params: this.safeParams(event.pathParameters),
111
+ params: event.pathParameters,
65
112
  cookies,
66
113
  event,
67
114
  context,
68
- isBase64Encoded: event.isBase64Encoded || false,
115
+ rawBody,
116
+ path: event.path,
117
+ isBase64Encoded: event.isBase64Encoded,
69
118
  requestId: context.awsRequestId,
70
119
  stage: event.requestContext?.stage || '$default',
71
120
  sourceIp: this.getSourceIp(event),
72
- userAgent: this.getUserAgent(event),
121
+ _startTime: Date.now(),
122
+ userAgent: typeof event.headers['user-agent'] === 'string'
123
+ ? event.headers['user-agent']
124
+ : event.headers['user-agent']?.[0] || 'unknown',
73
125
  };
74
126
  }
75
- static createResponseBody(response) {
76
- let body = response;
77
- if (response && typeof response === 'object') {
78
- if ('data' in response) {
79
- body = {
80
- data: response.data,
81
- timestamp: new Date().toISOString(),
82
- };
83
- }
84
- else if (response.body) {
85
- return response;
86
- }
87
- else {
88
- body = {
89
- data: response,
90
- timestamp: new Date().toISOString(),
91
- };
92
- }
93
- }
94
- return body;
95
- }
96
- static toLambdaResponse(response, request) {
97
- let statusCode = response.status ?? 200;
98
- let body = this.createResponseBody(response);
99
- let headers = {
127
+ static toLambdaResponse(appResponse, request, res, eventType) {
128
+ const statusCode = appResponse.status || 200;
129
+ const headers = {
100
130
  'Content-Type': 'application/json',
101
- 'X-Request-Id': request?.requestId || 'unknown',
102
- 'X-Powered-By': 'Lambda Decorators',
131
+ 'X-Request-Id': request.requestId,
132
+ ...(appResponse.headers || {}),
133
+ ...res.headers,
103
134
  };
104
- const cookies = [];
105
- if (response && typeof response === 'object') {
106
- if ('data' in response) {
107
- body = {
108
- success: _constants_1.OK_STATUSES.includes(statusCode),
109
- data: response.data,
110
- timestamp: new Date().toISOString(),
111
- requestId: request?.requestId,
112
- };
113
- }
114
- else if (response.body) {
115
- return response;
135
+ const originHeader = request.headers['origin'] || request.headers['Origin'];
136
+ let origin;
137
+ if (originHeader) {
138
+ if (Array.isArray(originHeader)) {
139
+ origin = originHeader[0];
116
140
  }
117
141
  else {
118
- body = {
119
- success: _constants_1.OK_STATUSES.includes(statusCode),
120
- data: response,
121
- timestamp: new Date().toISOString(),
122
- requestId: request?.requestId,
123
- };
142
+ origin = originHeader;
124
143
  }
125
144
  }
126
- let origin = LambdaAdapter.getHeaderValue(request?.headers, 'origin');
127
- const originHeader = Array.isArray(origin) ? origin[0] : origin;
128
- if (originHeader) {
129
- headers['Access-Control-Allow-Origin'] = originHeader;
145
+ if (origin) {
146
+ headers['Access-Control-Allow-Origin'] = origin;
130
147
  headers['Access-Control-Allow-Credentials'] = 'true';
131
148
  headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS';
132
149
  headers['Access-Control-Allow-Headers'] =
133
150
  'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token';
134
151
  }
135
- if (request?.cookies && Object.keys(request.cookies).length > 0) {
136
- Object.entries(request.cookies).forEach(([name, value]) => {
137
- cookies.push(`${name}=${encodeURIComponent(value)}; Path=/; HttpOnly`);
138
- });
139
- }
140
- const lambdaResponse = {
152
+ const body = JSON.stringify({
153
+ success: statusCode < 400,
154
+ data: appResponse.data,
155
+ timestamp: new Date().toISOString(),
156
+ });
157
+ const commonResponse = {
141
158
  statusCode,
142
159
  headers,
143
- body: JSON.stringify(body),
144
- isBase64Encoded: false,
160
+ body: appResponse.data,
161
+ timestamp: new Date().toISOString(),
145
162
  };
146
- if (cookies.length > 0) {
147
- lambdaResponse.cookies = cookies;
148
- }
149
- if (cookies.length > 0) {
150
- lambdaResponse.multiValueHeaders = {
151
- 'Set-Cookie': cookies,
152
- };
153
- }
154
- return lambdaResponse;
155
- }
156
- static createHandler(Contoller) {
157
- const handler = async (event, context) => {
158
- const instance = new Contoller();
159
- if (Object.hasOwn(instance, 'beforeStart')) {
160
- await instance.beforeStart?.();
161
- }
162
- try {
163
- const request = LambdaAdapter.toRequest(event, context);
164
- if (typeof instance.handleRequest === 'function') {
165
- const response = await instance.handleRequest(request);
166
- return LambdaAdapter.toLambdaResponse(response, request);
167
- }
168
- else {
169
- throw new Error('Controller must have handleRequest method');
170
- }
171
- }
172
- catch (error) {
173
- console.error('❌ Lambda error:', error);
174
- const errorResponse = {
175
- statusCode: error.status || 500,
176
- headers: {
177
- 'Content-Type': 'application/json',
178
- 'X-Request-Id': context.awsRequestId,
179
- 'Access-Control-Allow-Origin': event.headers?.origin || event.headers?.Origin || '*',
180
- 'Access-Control-Allow-Credentials': 'true',
181
- },
182
- body: JSON.stringify({
183
- success: false,
184
- message: error.message || 'Internal Server Error',
185
- requestId: context.awsRequestId,
186
- }),
163
+ switch (eventType) {
164
+ case 'rest':
165
+ return { ...commonResponse, isBase64Encoded: false };
166
+ case 'http':
167
+ return {
168
+ ...commonResponse,
169
+ cookies: request.cookies
170
+ ? Object.entries(request.cookies).map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
171
+ : undefined,
187
172
  };
188
- if (error.cookies) {
189
- errorResponse.cookies = error.cookies;
190
- }
191
- return errorResponse;
192
- }
193
- };
194
- return handler;
195
- }
196
- static safeHeaders(headers) {
197
- const result = {};
198
- if (!headers)
199
- return result;
200
- Object.entries(headers).forEach(([key, value]) => {
201
- if (value !== undefined) {
202
- result[key] = value;
203
- }
204
- });
205
- return result;
173
+ case 'url':
174
+ return {
175
+ ...commonResponse,
176
+ cookies: request.cookies
177
+ ? Object.entries(request.cookies).map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
178
+ : undefined,
179
+ };
180
+ default:
181
+ return {
182
+ statusCode,
183
+ headers,
184
+ body,
185
+ };
186
+ }
206
187
  }
207
- static safeParams(params) {
208
- const result = {};
209
- if (!params)
210
- return result;
211
- Object.entries(params).forEach(([key, value]) => {
212
- if (value !== undefined) {
213
- result[key] = value;
214
- }
188
+ static handleError(error, event, context) {
189
+ const eventType = this.getEventType(event);
190
+ const statusCode = error.status || 500;
191
+ const body = JSON.stringify({
192
+ success: false,
193
+ message: error.message || 'Internal Server Error',
194
+ requestId: context.awsRequestId,
215
195
  });
216
- return result;
196
+ const headers = {
197
+ 'Content-Type': 'application/json',
198
+ 'X-Request-Id': context.awsRequestId,
199
+ 'Access-Control-Allow-Origin': '*',
200
+ 'Access-Control-Allow-Credentials': 'true',
201
+ };
202
+ switch (eventType) {
203
+ case 'rest':
204
+ return {
205
+ statusCode,
206
+ headers,
207
+ body,
208
+ isBase64Encoded: false,
209
+ };
210
+ default:
211
+ return {
212
+ statusCode,
213
+ headers,
214
+ body,
215
+ };
216
+ }
217
217
  }
218
218
  static getSourceIp(event) {
219
- if (event.requestContext?.identity?.sourceIp) {
220
- return event.requestContext.identity.sourceIp;
221
- }
222
- const forwardedFor = event.headers?.['x-forwarded-for'];
219
+ const forwardedFor = event.headers['x-forwarded-for'];
223
220
  if (forwardedFor) {
224
- if (typeof forwardedFor === 'string') {
225
- return forwardedFor.split(',')[0].trim();
226
- }
227
- if (Array.isArray(forwardedFor) && forwardedFor.length > 0) {
221
+ if (Array.isArray(forwardedFor)) {
228
222
  return forwardedFor[0].split(',')[0].trim();
229
223
  }
224
+ return forwardedFor.split(',')[0].trim();
225
+ }
226
+ if (event.requestContext?.identity?.sourceIp) {
227
+ return event.requestContext.identity.sourceIp;
228
+ }
229
+ if (event.requestContext?.http?.sourceIp) {
230
+ return event.requestContext.http.sourceIp;
230
231
  }
231
232
  return '0.0.0.0';
232
233
  }
233
- static getUserAgent(event) {
234
- const ua = event.headers?.['user-agent'];
235
- if (!ua)
236
- return 'unknown';
237
- if (typeof ua === 'string')
238
- return ua;
239
- if (Array.isArray(ua) && ua.length > 0)
240
- return ua[0];
241
- return 'unknown';
242
- }
243
234
  }
244
235
  exports.LambdaAdapter = LambdaAdapter;
@@ -0,0 +1,7 @@
1
+ import { AppRequest } from '../../../types/index.js';
2
+ import { IncomingHttpHeaders } from 'http';
3
+ export declare class Response {
4
+ headers: IncomingHttpHeaders;
5
+ constructor(request: AppRequest);
6
+ setHeader(header: string, value: string): void;
7
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Response = void 0;
4
+ class Response {
5
+ headers;
6
+ constructor(request) {
7
+ this.headers = { ...request.headers };
8
+ }
9
+ setHeader(header, value) {
10
+ this.headers[header] = value;
11
+ }
12
+ }
13
+ exports.Response = Response;
@@ -101,6 +101,16 @@ class HttpServer extends Socket_1.Socket {
101
101
  const startTime = Date.now();
102
102
  try {
103
103
  const request = await this.createRequest(req);
104
+ let handledCors = { permitted: true, continue: true };
105
+ if (this.config.cors) {
106
+ handledCors = (0, _utils_1.handleCORS)(request, res, this.config.cors);
107
+ }
108
+ if (!handledCors.permitted) {
109
+ return this.sendResponse(res, { status: 403, message: 'CORS: Origin not allowed' }, startTime);
110
+ }
111
+ if (!handledCors.continue && handledCors.permitted) {
112
+ return this.sendResponse(res, { status: 204 }, startTime);
113
+ }
104
114
  let appRequest = await this.applyMiddlewares(request, req, res);
105
115
  let data = await this.findController(appRequest, req, res);
106
116
  const isError = !_constants_1.OK_STATUSES.includes(data.status);
@@ -12,6 +12,7 @@ function Server(config = {}) {
12
12
  ...config,
13
13
  controllers: [...(existingConfig.controllers || []), ...(config.controllers || [])],
14
14
  middlewares: [...(existingConfig.middlewares ?? []), ...(config.middlewares ?? [])],
15
+ cors: config.cors,
15
16
  interceptors: existingConfig.interceptor ?? config.interceptor,
16
17
  };
17
18
  Reflect.defineMetadata(_constants_1.SERVER_CONFIG_KEY, mergedConfig, target);
@@ -20,3 +20,4 @@ export declare const OK_STATUSES: number[];
20
20
  export declare const TO_VALIDATE: string[];
21
21
  export declare const STATISTIC: Record<'controllers' | 'routes', number>;
22
22
  export declare const INCREMENT_STATISTIC: (prop: "controllers" | "routes") => void;
23
+ export declare const CORS_METADATA = "cors:config";
package/dist/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.INCREMENT_STATISTIC = exports.STATISTIC = exports.TO_VALIDATE = exports.OK_STATUSES = exports.STOPPED = exports.CATCH = exports.INTECEPT = exports.WS_SERVICE_KEY = exports.WS_TOPIC_KEY = exports.WS_METADATA_KEY = exports.USE_MIDDLEWARE = exports.SERVER_MODULES_KEY = exports.SERVER_CONFIG_KEY = exports.OK_METADATA_KEY = exports.ENDPOINT = exports.INTERCEPTOR = exports.CONTROLLERS = exports.MIDDLEWARES = exports.ROUTE_MIDDLEWARES = exports.ROUTE_PREFIX = exports.APP_METADATA_KEY = exports.PARAM_METADATA_KEY = void 0;
3
+ exports.CORS_METADATA = exports.INCREMENT_STATISTIC = exports.STATISTIC = exports.TO_VALIDATE = exports.OK_STATUSES = exports.STOPPED = exports.CATCH = exports.INTECEPT = exports.WS_SERVICE_KEY = exports.WS_TOPIC_KEY = exports.WS_METADATA_KEY = exports.USE_MIDDLEWARE = exports.SERVER_MODULES_KEY = exports.SERVER_CONFIG_KEY = exports.OK_METADATA_KEY = exports.ENDPOINT = exports.INTERCEPTOR = exports.CONTROLLERS = exports.MIDDLEWARES = exports.ROUTE_MIDDLEWARES = exports.ROUTE_PREFIX = exports.APP_METADATA_KEY = exports.PARAM_METADATA_KEY = void 0;
4
4
  exports.PARAM_METADATA_KEY = 'design:parameters';
5
5
  exports.APP_METADATA_KEY = 'app:configuration';
6
6
  exports.ROUTE_PREFIX = 'route:prefix';
@@ -34,3 +34,4 @@ const INCREMENT_STATISTIC = (prop) => {
34
34
  exports.STATISTIC[prop] = exports.STATISTIC[prop] + 1;
35
35
  };
36
36
  exports.INCREMENT_STATISTIC = INCREMENT_STATISTIC;
37
+ exports.CORS_METADATA = 'cors:config';