quantum-flow 1.3.10 → 1.5.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/README.md +22 -15
- package/dist/app/aws/lambda.d.ts +0 -3
- package/dist/app/aws/lambda.js +25 -112
- package/dist/app/aws/utils/helpers.d.ts +4 -0
- package/dist/app/aws/utils/helpers.js +83 -0
- package/dist/app/aws/utils/index.d.ts +3 -0
- package/dist/app/aws/utils/index.js +19 -0
- package/dist/app/aws/utils/request.d.ts +23 -0
- package/dist/app/aws/utils/request.js +96 -0
- package/dist/app/aws/utils/response.d.ts +14 -0
- package/dist/app/aws/utils/response.js +37 -0
- package/dist/app/http/Application.d.ts +1 -1
- package/dist/app/http/Application.js +36 -21
- package/dist/app/http/decorators.js +1 -0
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -1
- package/dist/core/Controller.d.ts +6 -25
- package/dist/core/Controller.js +84 -118
- package/dist/core/Endpoint.js +1 -2
- package/dist/core/index.d.ts +1 -1
- package/dist/core/utils/index.d.ts +0 -2
- package/dist/core/utils/index.js +0 -2
- package/dist/examples/app.d.ts +0 -1
- package/dist/examples/app.js +70 -5
- package/dist/examples/controllers/user.js +44 -8
- package/dist/examples/controllers/userMetadata.d.ts +1 -1
- package/dist/examples/controllers/userMetadata.js +59 -9
- package/dist/middlewares/catch.d.ts +2 -0
- package/dist/middlewares/catch.js +10 -0
- package/dist/middlewares/cors.d.ts +2 -0
- package/dist/middlewares/cors.js +21 -0
- package/dist/middlewares/index.d.ts +5 -0
- package/dist/middlewares/index.js +21 -0
- package/dist/middlewares/sanitize.d.ts +2 -0
- package/dist/middlewares/sanitize.js +15 -0
- package/dist/{core/utils/helpers.js → middlewares/status.js} +1 -1
- package/dist/middlewares/use.d.ts +2 -0
- package/dist/middlewares/use.js +11 -0
- package/dist/types/common.d.ts +15 -32
- package/dist/types/controller.d.ts +7 -5
- package/dist/types/cors.d.ts +10 -0
- package/dist/types/cors.js +2 -0
- package/dist/types/http.d.ts +4 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.js +3 -0
- package/dist/types/lambda.d.ts +7 -17
- package/dist/types/multipart.d.ts +8 -0
- package/dist/types/multipart.js +2 -0
- package/dist/types/sanitize.d.ts +8 -0
- package/dist/types/sanitize.js +2 -0
- package/dist/utils/controller.d.ts +27 -3
- package/dist/utils/controller.js +122 -23
- package/dist/utils/cors.d.ts +8 -0
- package/dist/utils/cors.js +127 -0
- package/dist/utils/headers.d.ts +2 -0
- package/dist/utils/headers.js +9 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/multipart.d.ts +2 -8
- package/dist/utils/multipart.js +98 -2
- package/dist/utils/sanitize.d.ts +30 -0
- package/dist/utils/sanitize.js +134 -0
- package/dist/utils/server.js +5 -0
- package/package.json +9 -2
- package/dist/core/utils/middlewares.d.ts +0 -3
- package/dist/core/utils/middlewares.js +0 -22
- /package/dist/{core/utils/helpers.d.ts → middlewares/status.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ You can use controllers and server functionality by importing controllers and cr
|
|
|
20
20
|
- `quantum-flow/http` - Main application source code for HTTP servers.
|
|
21
21
|
- `quantum-flow/aws` - Main application source code AWS Lambda.
|
|
22
22
|
- `quantum-flow/core` - Core framework components like Controller and Endpoint.
|
|
23
|
+
- `quantum-flow/middlewares` - Core middlewares to use within the application.
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
@@ -30,7 +31,6 @@ Use the `@Controller` decorator to define controllers with options such as prefi
|
|
|
30
31
|
```typescript
|
|
31
32
|
import {
|
|
32
33
|
Body,
|
|
33
|
-
Catch,
|
|
34
34
|
Controller,
|
|
35
35
|
Headers,
|
|
36
36
|
InjectWS,
|
|
@@ -41,9 +41,10 @@ import {
|
|
|
41
41
|
Request,
|
|
42
42
|
Response,
|
|
43
43
|
Status,
|
|
44
|
-
USE
|
|
44
|
+
USE,
|
|
45
45
|
} from 'quantum-flow/core';
|
|
46
46
|
import {IsString} from 'class-validator'
|
|
47
|
+
import { Catch, Cors, Sanitize, Use } from 'quantum-flow/middlewares';
|
|
47
48
|
|
|
48
49
|
class UserDto {
|
|
49
50
|
constructor() {}
|
|
@@ -54,14 +55,24 @@ class UserDto {
|
|
|
54
55
|
@Controller({
|
|
55
56
|
prefix: 'user',
|
|
56
57
|
controllers: [UserMetadata, ...],
|
|
57
|
-
interceptor: (data, req, res) =>
|
|
58
|
-
return { data, intercepted: true };
|
|
59
|
-
},
|
|
58
|
+
interceptor: (data, req, res) => data,
|
|
60
59
|
})
|
|
60
|
+
@Cors({ origin: '*' })
|
|
61
61
|
@Catch((err) => ({ status: 500, err }))
|
|
62
|
+
@Use(()=>{})
|
|
63
|
+
@Sanitize({
|
|
64
|
+
schema: Joi.object({
|
|
65
|
+
name: Joi.string().trim().min(2).max(50).required(),
|
|
66
|
+
}),
|
|
67
|
+
action: 'both',
|
|
68
|
+
options: { abortEarly: false },
|
|
69
|
+
stripUnknown: true,
|
|
70
|
+
type: 'body',
|
|
71
|
+
})
|
|
62
72
|
export class User {
|
|
63
73
|
@Status(201)
|
|
64
|
-
@PUT(':id',[...middlewares])
|
|
74
|
+
@PUT(':id',[(req, res)=>{} , ...middlewares])
|
|
75
|
+
@Cors({ origin: '*' })
|
|
65
76
|
async createUser(
|
|
66
77
|
@Body(UserDto) body: UserDto,
|
|
67
78
|
@Query() query: any,
|
|
@@ -84,7 +95,7 @@ export class User {
|
|
|
84
95
|
prefix: 'api',
|
|
85
96
|
controllers: [UserController, SocketController],
|
|
86
97
|
middelwares: [...middlewares],
|
|
87
|
-
interceptor: (
|
|
98
|
+
interceptor: (data, req, res) => data,
|
|
88
99
|
})
|
|
89
100
|
@Catch((error) => ({ status: 400, error }))
|
|
90
101
|
class RootController {}
|
|
@@ -97,11 +108,10 @@ Use the `@Server` decorator with configuration options like port, host, controll
|
|
|
97
108
|
```typescript
|
|
98
109
|
import { Server, Port, Host, Use, Catch, HttpServer } from 'quantum-flow/http';
|
|
99
110
|
|
|
100
|
-
@Server({ controllers: [RootController] })
|
|
111
|
+
@Server({ controllers: [RootController], cors: { origin: '*' } })
|
|
101
112
|
@Port(3000)
|
|
102
113
|
@Host('localhost')
|
|
103
114
|
@Use((data) => data)
|
|
104
|
-
@Use((data) => data)
|
|
105
115
|
@Catch((error) => ({ status: 400, error }))
|
|
106
116
|
class App {}
|
|
107
117
|
|
|
@@ -115,6 +125,8 @@ server.listen().catch(console.error);
|
|
|
115
125
|
- Use `@Use` to apply middlewares.
|
|
116
126
|
- Use `@Catch` to handle errors.
|
|
117
127
|
- Use `@Port` and `@Host` to configure server port and host.
|
|
128
|
+
- Use `@Cors` to configure cors.
|
|
129
|
+
- Use `@Sanitize` to apply sanitization to reqest.
|
|
118
130
|
|
|
119
131
|
## Request decorators
|
|
120
132
|
|
|
@@ -160,8 +172,6 @@ Enable WebSocket in the server configuration and register WebSocket controllers.
|
|
|
160
172
|
export class Socket {
|
|
161
173
|
@OnConnection()
|
|
162
174
|
onConnection(event: WebSocketEvent) {
|
|
163
|
-
console.log(`✅ Connected: ${event.client.id}`);
|
|
164
|
-
|
|
165
175
|
// Send greeting ONLY to this client
|
|
166
176
|
event.client.socket.send(
|
|
167
177
|
JSON.stringify({
|
|
@@ -181,8 +191,6 @@ export class Socket {
|
|
|
181
191
|
// The message is ALREADY automatically broadcast to all!
|
|
182
192
|
|
|
183
193
|
const msg = event.message?.data;
|
|
184
|
-
console.log(`💬 Message in chat: ${msg?.text}`);
|
|
185
|
-
|
|
186
194
|
// You can add logic, but no need to broadcast
|
|
187
195
|
if (msg?.text.includes('bad')) {
|
|
188
196
|
// If return empty, the message will not be sent
|
|
@@ -197,7 +205,6 @@ export class Socket {
|
|
|
197
205
|
*/
|
|
198
206
|
@Subscribe('news')
|
|
199
207
|
onNewsMessage(event: WebSocketEvent) {
|
|
200
|
-
console.log(`📰 News: ${event.message?.data.title}`);
|
|
201
208
|
// Automatic broadcast to all subscribed to 'news'
|
|
202
209
|
}
|
|
203
210
|
|
|
@@ -221,7 +228,6 @@ export class Socket {
|
|
|
221
228
|
@OnMessage('subscribe')
|
|
222
229
|
onSubscribe(event: WebSocketEvent) {
|
|
223
230
|
const topic = event.message?.data.topic;
|
|
224
|
-
console.log(`📌 Client ${event.client.id} subscribed to ${topic}`);
|
|
225
231
|
|
|
226
232
|
// Server will save the subscription automatically, no need to do anything!
|
|
227
233
|
// Just confirm
|
|
@@ -241,6 +247,7 @@ export class Socket {
|
|
|
241
247
|
### Use
|
|
242
248
|
|
|
243
249
|
Class decorator to add global middlewares to the server.
|
|
250
|
+
Should be used only with @Server decorator
|
|
244
251
|
|
|
245
252
|
```typescript
|
|
246
253
|
@Use(middleware)
|
package/dist/app/aws/lambda.d.ts
CHANGED
|
@@ -2,9 +2,6 @@ import { LambdaApp } from '../../types/index.js';
|
|
|
2
2
|
import { Handler } from 'aws-lambda';
|
|
3
3
|
export declare class LambdaAdapter {
|
|
4
4
|
static createHandler(Controller: new (...args: any[]) => LambdaApp): Handler;
|
|
5
|
-
private static getEventType;
|
|
6
|
-
private static toRequest;
|
|
7
5
|
private static toLambdaResponse;
|
|
8
6
|
private static handleError;
|
|
9
|
-
private static getSourceIp;
|
|
10
7
|
}
|
package/dist/app/aws/lambda.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LambdaAdapter = void 0;
|
|
4
|
+
const _constants_1 = require("../../constants.js");
|
|
4
5
|
const _utils_1 = require("../../utils/index.js");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
5
7
|
class LambdaAdapter {
|
|
6
8
|
static createHandler(Controller) {
|
|
7
9
|
return async (event, context) => {
|
|
@@ -9,112 +11,39 @@ class LambdaAdapter {
|
|
|
9
11
|
if (Object.hasOwn(instance, 'beforeStart')) {
|
|
10
12
|
await instance.beforeStart?.();
|
|
11
13
|
}
|
|
14
|
+
const eventType = (0, utils_1.getEventType)(event);
|
|
12
15
|
try {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
+
const cors = Reflect.getMetadata(_constants_1.CORS_METADATA, instance);
|
|
17
|
+
const request = new utils_1.LRequest(event, context);
|
|
18
|
+
const response = new utils_1.LResponse();
|
|
19
|
+
let handledCors = { permitted: true, continue: true };
|
|
20
|
+
if (cors) {
|
|
21
|
+
handledCors = (0, _utils_1.handleCORS)(request, response, cors);
|
|
22
|
+
}
|
|
23
|
+
if (!handledCors.permitted) {
|
|
24
|
+
return this.toLambdaResponse({ status: 403, message: 'Cors: Origin not allowed' }, request, response, eventType);
|
|
25
|
+
}
|
|
26
|
+
if (!handledCors.continue && handledCors.permitted) {
|
|
27
|
+
return this.toLambdaResponse({ status: 204 }, request, response, eventType);
|
|
28
|
+
}
|
|
16
29
|
if (typeof instance.handleRequest !== 'function') {
|
|
17
30
|
throw new Error('Controller must have handleRequest method');
|
|
18
31
|
}
|
|
19
|
-
const
|
|
20
|
-
return this.toLambdaResponse(
|
|
32
|
+
const data = await instance.handleRequest(request, response);
|
|
33
|
+
return this.toLambdaResponse(data, request, response, eventType);
|
|
21
34
|
}
|
|
22
35
|
catch (error) {
|
|
23
36
|
return this.handleError(error, event, context);
|
|
24
37
|
}
|
|
25
38
|
};
|
|
26
39
|
}
|
|
27
|
-
static
|
|
28
|
-
|
|
29
|
-
return 'rest';
|
|
30
|
-
}
|
|
31
|
-
if (event.version === '2.0' || event.requestContext?.http) {
|
|
32
|
-
return 'http';
|
|
33
|
-
}
|
|
34
|
-
if (event.version && event.rawPath && !event.requestContext?.http) {
|
|
35
|
-
return 'url';
|
|
36
|
-
}
|
|
37
|
-
return 'rest';
|
|
38
|
-
}
|
|
39
|
-
static toRequest(event, context) {
|
|
40
|
-
const query = {};
|
|
41
|
-
if (event.multiValueQueryStringParameters) {
|
|
42
|
-
Object.entries(event.multiValueQueryStringParameters).forEach(([key, value]) => {
|
|
43
|
-
query[key] = value;
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
Object.entries(event.queryStringParameters).forEach(([key, value]) => {
|
|
48
|
-
query[key] = value;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
const cookies = {};
|
|
52
|
-
const cookieHeader = event.headers?.Cookie || event.headers?.cookie || event.headers?.cookies;
|
|
53
|
-
if (cookieHeader) {
|
|
54
|
-
if (Array.isArray(cookieHeader)) {
|
|
55
|
-
cookieHeader.forEach((cookie) => {
|
|
56
|
-
const [name, value] = cookie.split('=');
|
|
57
|
-
if (name && value)
|
|
58
|
-
cookies[name] = decodeURIComponent(value);
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
cookieHeader.split(';').forEach((cookie) => {
|
|
63
|
-
const [name, value] = cookie.trim().split('=');
|
|
64
|
-
if (name && value)
|
|
65
|
-
cookies[name] = decodeURIComponent(value);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
let rawBody = Buffer.from(event.body ?? '', 'base64');
|
|
70
|
-
let body = event.body || {};
|
|
71
|
-
if (event.body && event.isBase64Encoded) {
|
|
72
|
-
body = rawBody.toString('utf-8');
|
|
73
|
-
}
|
|
74
|
-
if (typeof body === 'string' && body.trim().startsWith('{')) {
|
|
75
|
-
try {
|
|
76
|
-
body = JSON.parse(body);
|
|
77
|
-
}
|
|
78
|
-
catch (e) { }
|
|
79
|
-
}
|
|
80
|
-
const xForvarded = Array.isArray(event.headers['x-forwarded-proto'])
|
|
81
|
-
? event.headers['x-forwarded-proto']?.[0]
|
|
82
|
-
: event.headers['x-forwarded-proto'];
|
|
83
|
-
const xhost = Array.isArray(event.headers['host'])
|
|
84
|
-
? event.headers['host']?.[0]
|
|
85
|
-
: event.headers['host'];
|
|
86
|
-
const protocol = xForvarded || 'https';
|
|
87
|
-
const host = xhost || 'localhost:3000';
|
|
88
|
-
const fullUrl = `${protocol}://${host}${event.path}`;
|
|
89
|
-
let url = new URL(fullUrl);
|
|
90
|
-
return {
|
|
91
|
-
method: event.httpMethod.toUpperCase(),
|
|
92
|
-
url,
|
|
93
|
-
headers: event.headers,
|
|
94
|
-
query,
|
|
95
|
-
body,
|
|
96
|
-
params: event.pathParameters,
|
|
97
|
-
cookies,
|
|
98
|
-
event,
|
|
99
|
-
context,
|
|
100
|
-
rawBody,
|
|
101
|
-
path: event.path,
|
|
102
|
-
isBase64Encoded: event.isBase64Encoded,
|
|
103
|
-
requestId: context.awsRequestId,
|
|
104
|
-
stage: event.requestContext?.stage || '$default',
|
|
105
|
-
sourceIp: this.getSourceIp(event),
|
|
106
|
-
_startTime: Date.now(),
|
|
107
|
-
userAgent: typeof event.headers['user-agent'] === 'string'
|
|
108
|
-
? event.headers['user-agent']
|
|
109
|
-
: event.headers['user-agent']?.[0] || 'unknown',
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
static toLambdaResponse(response, request, eventType) {
|
|
113
|
-
const statusCode = response.status || 200;
|
|
40
|
+
static toLambdaResponse(data, request, response, eventType) {
|
|
41
|
+
const statusCode = data.status || 200;
|
|
114
42
|
const headers = {
|
|
115
43
|
'Content-Type': 'application/json',
|
|
116
44
|
'X-Request-Id': request.requestId,
|
|
117
|
-
...(
|
|
45
|
+
...(data.headers || {}),
|
|
46
|
+
...response.headers,
|
|
118
47
|
};
|
|
119
48
|
const originHeader = request.headers['origin'] || request.headers['Origin'];
|
|
120
49
|
let origin;
|
|
@@ -135,13 +64,13 @@ class LambdaAdapter {
|
|
|
135
64
|
}
|
|
136
65
|
const body = JSON.stringify({
|
|
137
66
|
success: statusCode < 400,
|
|
138
|
-
data:
|
|
67
|
+
data: data.data ?? data.error,
|
|
139
68
|
timestamp: new Date().toISOString(),
|
|
140
69
|
});
|
|
141
70
|
const commonResponse = {
|
|
142
71
|
statusCode,
|
|
143
72
|
headers,
|
|
144
|
-
body:
|
|
73
|
+
body: data.data ?? data.error,
|
|
145
74
|
timestamp: new Date().toISOString(),
|
|
146
75
|
};
|
|
147
76
|
switch (eventType) {
|
|
@@ -170,7 +99,7 @@ class LambdaAdapter {
|
|
|
170
99
|
}
|
|
171
100
|
}
|
|
172
101
|
static handleError(error, event, context) {
|
|
173
|
-
const eventType =
|
|
102
|
+
const eventType = (0, utils_1.getEventType)(event);
|
|
174
103
|
const statusCode = error.status || 500;
|
|
175
104
|
const body = JSON.stringify({
|
|
176
105
|
success: false,
|
|
@@ -199,21 +128,5 @@ class LambdaAdapter {
|
|
|
199
128
|
};
|
|
200
129
|
}
|
|
201
130
|
}
|
|
202
|
-
static getSourceIp(event) {
|
|
203
|
-
const forwardedFor = event.headers['x-forwarded-for'];
|
|
204
|
-
if (forwardedFor) {
|
|
205
|
-
if (Array.isArray(forwardedFor)) {
|
|
206
|
-
return forwardedFor[0].split(',')[0].trim();
|
|
207
|
-
}
|
|
208
|
-
return forwardedFor.split(',')[0].trim();
|
|
209
|
-
}
|
|
210
|
-
if (event.requestContext?.identity?.sourceIp) {
|
|
211
|
-
return event.requestContext.identity.sourceIp;
|
|
212
|
-
}
|
|
213
|
-
if (event.requestContext?.http?.sourceIp) {
|
|
214
|
-
return event.requestContext.http.sourceIp;
|
|
215
|
-
}
|
|
216
|
-
return '0.0.0.0';
|
|
217
|
-
}
|
|
218
131
|
}
|
|
219
132
|
exports.LambdaAdapter = LambdaAdapter;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { NormalizedEvent } from '../../../types/index.js';
|
|
2
|
+
export declare const getEventType: (event: any) => "rest" | "http" | "url";
|
|
3
|
+
export declare const getSourceIp: (event: NormalizedEvent) => string;
|
|
4
|
+
export declare const normalizeEvent: (event: any, type: string) => NormalizedEvent;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeEvent = exports.getSourceIp = exports.getEventType = void 0;
|
|
4
|
+
const getEventType = (event) => {
|
|
5
|
+
if (event.httpMethod && event.resource) {
|
|
6
|
+
return 'rest';
|
|
7
|
+
}
|
|
8
|
+
if (event.version === '2.0' || event.requestContext?.http) {
|
|
9
|
+
return 'http';
|
|
10
|
+
}
|
|
11
|
+
if (event.version && event.rawPath && !event.requestContext?.http) {
|
|
12
|
+
return 'url';
|
|
13
|
+
}
|
|
14
|
+
return 'rest';
|
|
15
|
+
};
|
|
16
|
+
exports.getEventType = getEventType;
|
|
17
|
+
const getSourceIp = (event) => {
|
|
18
|
+
const forwardedFor = event.headers['x-forwarded-for'];
|
|
19
|
+
if (forwardedFor) {
|
|
20
|
+
if (Array.isArray(forwardedFor)) {
|
|
21
|
+
return forwardedFor[0].split(',')[0].trim();
|
|
22
|
+
}
|
|
23
|
+
return forwardedFor.split(',')[0].trim();
|
|
24
|
+
}
|
|
25
|
+
if (event.requestContext?.identity?.sourceIp) {
|
|
26
|
+
return event.requestContext.identity.sourceIp;
|
|
27
|
+
}
|
|
28
|
+
if (event.requestContext?.http?.sourceIp) {
|
|
29
|
+
return event.requestContext.http.sourceIp;
|
|
30
|
+
}
|
|
31
|
+
return '0.0.0.0';
|
|
32
|
+
};
|
|
33
|
+
exports.getSourceIp = getSourceIp;
|
|
34
|
+
const parseQueryString = (queryString) => {
|
|
35
|
+
const params = {};
|
|
36
|
+
if (!queryString)
|
|
37
|
+
return params;
|
|
38
|
+
new URLSearchParams(queryString).forEach((value, key) => {
|
|
39
|
+
params[key] = value;
|
|
40
|
+
});
|
|
41
|
+
return params;
|
|
42
|
+
};
|
|
43
|
+
const normalizeEvent = (event, type) => {
|
|
44
|
+
const base = {
|
|
45
|
+
headers: event.headers || {},
|
|
46
|
+
body: event.body || null,
|
|
47
|
+
isBase64Encoded: event.isBase64Encoded || false,
|
|
48
|
+
requestContext: event.requestContext,
|
|
49
|
+
};
|
|
50
|
+
switch (type) {
|
|
51
|
+
case 'rest':
|
|
52
|
+
return {
|
|
53
|
+
...base,
|
|
54
|
+
httpMethod: event.httpMethod,
|
|
55
|
+
path: event.path,
|
|
56
|
+
queryStringParameters: event.queryStringParameters || {},
|
|
57
|
+
multiValueQueryStringParameters: event.multiValueQueryStringParameters,
|
|
58
|
+
pathParameters: event.pathParameters || {},
|
|
59
|
+
cookies: event.headers?.Cookie ? [event.headers.Cookie] : [],
|
|
60
|
+
};
|
|
61
|
+
case 'http':
|
|
62
|
+
return {
|
|
63
|
+
...base,
|
|
64
|
+
httpMethod: event.requestContext?.http?.method || 'GET',
|
|
65
|
+
path: event.rawPath,
|
|
66
|
+
queryStringParameters: event.queryStringParameters || {},
|
|
67
|
+
pathParameters: event.pathParameters || {},
|
|
68
|
+
cookies: event.cookies || [],
|
|
69
|
+
};
|
|
70
|
+
case 'url':
|
|
71
|
+
return {
|
|
72
|
+
...base,
|
|
73
|
+
httpMethod: event.requestContext?.http?.method || 'GET',
|
|
74
|
+
path: event.rawPath,
|
|
75
|
+
queryStringParameters: parseQueryString(event.rawQueryString),
|
|
76
|
+
pathParameters: {},
|
|
77
|
+
cookies: [],
|
|
78
|
+
};
|
|
79
|
+
default:
|
|
80
|
+
throw new Error(`Unsupported event type: ${type}`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
exports.normalizeEvent = normalizeEvent;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./helpers"), exports);
|
|
18
|
+
__exportStar(require("./request"), exports);
|
|
19
|
+
__exportStar(require("./response"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HTTP_METHODS, LambdaEvent, MultipartFile } from '../../../types/index.js';
|
|
2
|
+
import { Context } from 'aws-lambda';
|
|
3
|
+
export declare class LRequest {
|
|
4
|
+
requestUrl: URL;
|
|
5
|
+
method: HTTP_METHODS;
|
|
6
|
+
path?: string;
|
|
7
|
+
headers: Record<string, string | string[]>;
|
|
8
|
+
query?: Record<string, string | string[]>;
|
|
9
|
+
params?: Record<string, string>;
|
|
10
|
+
body: any;
|
|
11
|
+
rawBody: Buffer<ArrayBufferLike>;
|
|
12
|
+
isBase64Encoded?: boolean;
|
|
13
|
+
cookies: Record<string, string>;
|
|
14
|
+
multipart?: Record<string, MultipartFile | MultipartFile[]>;
|
|
15
|
+
_startTime: number;
|
|
16
|
+
url: string;
|
|
17
|
+
requestId: string;
|
|
18
|
+
stage?: string;
|
|
19
|
+
userAgent: string;
|
|
20
|
+
sourceIp: string;
|
|
21
|
+
constructor(lambdaEvent: LambdaEvent, context: Context);
|
|
22
|
+
end(): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LRequest = void 0;
|
|
4
|
+
const helpers_1 = require("./helpers");
|
|
5
|
+
class LRequest {
|
|
6
|
+
requestUrl;
|
|
7
|
+
method;
|
|
8
|
+
path;
|
|
9
|
+
headers;
|
|
10
|
+
query;
|
|
11
|
+
params;
|
|
12
|
+
body;
|
|
13
|
+
rawBody;
|
|
14
|
+
isBase64Encoded;
|
|
15
|
+
cookies;
|
|
16
|
+
multipart;
|
|
17
|
+
_startTime;
|
|
18
|
+
url;
|
|
19
|
+
requestId;
|
|
20
|
+
stage;
|
|
21
|
+
userAgent;
|
|
22
|
+
sourceIp;
|
|
23
|
+
constructor(lambdaEvent, context) {
|
|
24
|
+
const event = (0, helpers_1.normalizeEvent)(lambdaEvent, (0, helpers_1.getEventType)(lambdaEvent));
|
|
25
|
+
const query = {};
|
|
26
|
+
if (event.multiValueQueryStringParameters) {
|
|
27
|
+
Object.entries(event.multiValueQueryStringParameters).forEach(([key, value]) => {
|
|
28
|
+
query[key] = value;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
Object.entries(event.queryStringParameters).forEach(([key, value]) => {
|
|
33
|
+
query[key] = value;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const cookies = {};
|
|
37
|
+
const cookieHeader = event.headers?.Cookie || event.headers?.cookie || event.headers?.cookies;
|
|
38
|
+
if (cookieHeader) {
|
|
39
|
+
if (Array.isArray(cookieHeader)) {
|
|
40
|
+
cookieHeader.forEach((cookie) => {
|
|
41
|
+
const [name, value] = cookie.split('=');
|
|
42
|
+
if (name && value)
|
|
43
|
+
cookies[name] = decodeURIComponent(value);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
cookieHeader.split(';').forEach((cookie) => {
|
|
48
|
+
const [name, value] = cookie.trim().split('=');
|
|
49
|
+
if (name && value)
|
|
50
|
+
cookies[name] = decodeURIComponent(value);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
let rawBody = Buffer.from(event.body ?? '', 'base64');
|
|
55
|
+
let body = event.body || {};
|
|
56
|
+
if (event.body && event.isBase64Encoded) {
|
|
57
|
+
body = rawBody.toString('utf-8');
|
|
58
|
+
}
|
|
59
|
+
if (typeof body === 'string' && body.trim().startsWith('{')) {
|
|
60
|
+
try {
|
|
61
|
+
body = JSON.parse(body);
|
|
62
|
+
}
|
|
63
|
+
catch (e) { }
|
|
64
|
+
}
|
|
65
|
+
const xForvarded = Array.isArray(event.headers['x-forwarded-proto'])
|
|
66
|
+
? event.headers['x-forwarded-proto']?.[0]
|
|
67
|
+
: event.headers['x-forwarded-proto'];
|
|
68
|
+
const xhost = Array.isArray(event.headers['host'])
|
|
69
|
+
? event.headers['host']?.[0]
|
|
70
|
+
: event.headers['host'];
|
|
71
|
+
const protocol = xForvarded || 'https';
|
|
72
|
+
const host = xhost || 'localhost:3000';
|
|
73
|
+
const fullUrl = `${protocol}://${host}${event.path}`;
|
|
74
|
+
let url = new URL(fullUrl);
|
|
75
|
+
this.method = event.httpMethod.toUpperCase();
|
|
76
|
+
this.url = fullUrl;
|
|
77
|
+
this.headers = event.headers;
|
|
78
|
+
this.cookies = cookies;
|
|
79
|
+
this.params = event.pathParameters;
|
|
80
|
+
this.rawBody = rawBody;
|
|
81
|
+
this.path = event.path;
|
|
82
|
+
this.isBase64Encoded = event.isBase64Encoded;
|
|
83
|
+
this.requestId = context.awsRequestId;
|
|
84
|
+
this.requestUrl = url;
|
|
85
|
+
this.stage = event.requestContext?.stage || '$default';
|
|
86
|
+
this.sourceIp = (0, helpers_1.getSourceIp)(event);
|
|
87
|
+
this.userAgent =
|
|
88
|
+
typeof event.headers['user-agent'] === 'string'
|
|
89
|
+
? event.headers['user-agent']
|
|
90
|
+
: event.headers['user-agent']?.[0] || 'unknown';
|
|
91
|
+
}
|
|
92
|
+
end() {
|
|
93
|
+
console.log('NOT implemented for lambda');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
exports.LRequest = LRequest;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ServerResponse } from 'http';
|
|
2
|
+
export declare class LResponse {
|
|
3
|
+
private originalResponse?;
|
|
4
|
+
private _statusCode;
|
|
5
|
+
private _headers;
|
|
6
|
+
private _body;
|
|
7
|
+
constructor(originalResponse?: ServerResponse | undefined);
|
|
8
|
+
setHeader(name: string, value: string): void;
|
|
9
|
+
set statusCode(code: number);
|
|
10
|
+
get statusCode(): number;
|
|
11
|
+
get headers(): Record<string, string>;
|
|
12
|
+
send(): void;
|
|
13
|
+
get original(): ServerResponse | undefined;
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LResponse = void 0;
|
|
4
|
+
class LResponse {
|
|
5
|
+
originalResponse;
|
|
6
|
+
_statusCode = 200;
|
|
7
|
+
_headers = {};
|
|
8
|
+
_body = null;
|
|
9
|
+
constructor(originalResponse) {
|
|
10
|
+
this.originalResponse = originalResponse;
|
|
11
|
+
}
|
|
12
|
+
setHeader(name, value) {
|
|
13
|
+
this._headers[name] = value;
|
|
14
|
+
if (this.originalResponse) {
|
|
15
|
+
this.originalResponse.setHeader(name, value);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
set statusCode(code) {
|
|
19
|
+
this._statusCode = code;
|
|
20
|
+
if (this.originalResponse) {
|
|
21
|
+
this.originalResponse.statusCode = code;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
get statusCode() {
|
|
25
|
+
return this._statusCode;
|
|
26
|
+
}
|
|
27
|
+
get headers() {
|
|
28
|
+
return { ...this._headers };
|
|
29
|
+
}
|
|
30
|
+
send() {
|
|
31
|
+
throw `Lambda response doesn't have "send" method`;
|
|
32
|
+
}
|
|
33
|
+
get original() {
|
|
34
|
+
return this.originalResponse;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.LResponse = LResponse;
|