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
@@ -0,0 +1,8 @@
1
+ import { CORSConfig } from '../types/index.js';
2
+ export declare function handleCORS(req: any, res: any, config: CORSConfig): {
3
+ permitted: boolean;
4
+ continue: boolean;
5
+ };
6
+ export declare function isPreflightRequest(req: any): boolean;
7
+ export declare const getCORSConfig: (controller: any, methodName?: string) => CORSConfig | null;
8
+ export declare const getCORSHeaders: (req: any, config: CORSConfig) => Record<string, string>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCORSHeaders = exports.getCORSConfig = void 0;
4
+ exports.handleCORS = handleCORS;
5
+ exports.isPreflightRequest = isPreflightRequest;
6
+ const _constants_1 = require("../constants.js");
7
+ function handleCORS(req, res, config) {
8
+ const origin = req.headers.origin || req.headers.Origin || req.url.origin;
9
+ function isOriginAllowed() {
10
+ if (!origin)
11
+ return false;
12
+ if (config.origin === '*')
13
+ return true;
14
+ if (typeof config.origin === 'string')
15
+ return config.origin === origin;
16
+ if (Array.isArray(config.origin))
17
+ return config.origin.includes(origin);
18
+ if (typeof config.origin === 'function')
19
+ return config.origin(origin);
20
+ return false;
21
+ }
22
+ if (origin && !isOriginAllowed()) {
23
+ res.statusCode = 403;
24
+ res.setHeader('Content-Type', 'application/json');
25
+ return { permitted: false, continue: false };
26
+ }
27
+ if (req.method === 'OPTIONS') {
28
+ if (origin) {
29
+ res.setHeader('Access-Control-Allow-Origin', origin);
30
+ }
31
+ else if (config.origin === '*') {
32
+ res.setHeader('Access-Control-Allow-Origin', '*');
33
+ }
34
+ if (config.methods) {
35
+ res.setHeader('Access-Control-Allow-Methods', config.methods.join(', '));
36
+ }
37
+ if (config.allowedHeaders) {
38
+ res.setHeader('Access-Control-Allow-Headers', config.allowedHeaders.join(', '));
39
+ }
40
+ if (config.credentials) {
41
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
42
+ }
43
+ if (config.maxAge) {
44
+ res.setHeader('Access-Control-Max-Age', config.maxAge.toString());
45
+ }
46
+ res.statusCode = config.optionsSuccessStatus || 204;
47
+ return { permitted: true, continue: false };
48
+ }
49
+ if (origin) {
50
+ res.setHeader('Access-Control-Allow-Origin', origin);
51
+ if (config.credentials) {
52
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
53
+ }
54
+ if (config.exposedHeaders) {
55
+ res.setHeader('Access-Control-Expose-Headers', config.exposedHeaders.join(', '));
56
+ }
57
+ }
58
+ return { permitted: true, continue: true };
59
+ }
60
+ function isPreflightRequest(req) {
61
+ return (req.method === 'OPTIONS' && req.headers['access-control-request-method'] && req.headers.origin);
62
+ }
63
+ const getCORSConfig = (controller, methodName) => {
64
+ if (methodName) {
65
+ const methodConfig = Reflect.getMetadata(_constants_1.CORS_METADATA, controller, methodName);
66
+ if (methodConfig) {
67
+ return methodConfig;
68
+ }
69
+ }
70
+ if (controller && controller.constructor) {
71
+ const classConfig = Reflect.getMetadata(_constants_1.CORS_METADATA, controller.constructor.prototype);
72
+ if (classConfig) {
73
+ return classConfig;
74
+ }
75
+ }
76
+ return null;
77
+ };
78
+ exports.getCORSConfig = getCORSConfig;
79
+ const getCORSHeaders = (req, config) => {
80
+ const headers = {};
81
+ const origin = req?.headers?.origin || req?.headers?.Origin;
82
+ if (!origin)
83
+ return headers;
84
+ let allowedOrigin = null;
85
+ if (config.origin === '*') {
86
+ allowedOrigin = '*';
87
+ }
88
+ else if (typeof config.origin === 'string') {
89
+ allowedOrigin = config.origin;
90
+ }
91
+ else if (Array.isArray(config.origin)) {
92
+ if (config.origin.includes(origin)) {
93
+ allowedOrigin = origin;
94
+ }
95
+ }
96
+ else if (typeof config.origin === 'function') {
97
+ if (config.origin(origin)) {
98
+ allowedOrigin = origin;
99
+ }
100
+ }
101
+ else if (config.origin === true) {
102
+ allowedOrigin = origin;
103
+ }
104
+ if (allowedOrigin) {
105
+ headers['Access-Control-Allow-Origin'] = allowedOrigin;
106
+ if (config.credentials) {
107
+ headers['Access-Control-Allow-Credentials'] = 'true';
108
+ }
109
+ if (config.exposedHeaders?.length) {
110
+ headers['Access-Control-Expose-Headers'] = config.exposedHeaders.join(', ');
111
+ }
112
+ if (req.method === 'OPTIONS') {
113
+ if (config.methods?.length) {
114
+ headers['Access-Control-Allow-Methods'] = config.methods.join(', ');
115
+ }
116
+ if (config.allowedHeaders?.length) {
117
+ headers['Access-Control-Allow-Headers'] = config.allowedHeaders.join(', ');
118
+ }
119
+ if (config.maxAge) {
120
+ headers['Access-Control-Max-Age'] = config.maxAge.toString();
121
+ }
122
+ }
123
+ }
124
+ return headers;
125
+ };
126
+ exports.getCORSHeaders = getCORSHeaders;
@@ -1,8 +1,10 @@
1
1
  export * from './controller';
2
+ export * from './cors';
3
+ export * from './endpoint';
2
4
  export * from './helper';
5
+ export * from './lambda';
3
6
  export * from './multipart';
4
7
  export * from './parsers';
5
8
  export * from './server';
6
9
  export * from './transform';
7
10
  export * from './validate';
8
- export * from './endpoint';
@@ -15,10 +15,12 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./controller"), exports);
18
+ __exportStar(require("./cors"), exports);
19
+ __exportStar(require("./endpoint"), exports);
18
20
  __exportStar(require("./helper"), exports);
21
+ __exportStar(require("./lambda"), exports);
19
22
  __exportStar(require("./multipart"), exports);
20
23
  __exportStar(require("./parsers"), exports);
21
24
  __exportStar(require("./server"), exports);
22
25
  __exportStar(require("./transform"), exports);
23
26
  __exportStar(require("./validate"), exports);
24
- __exportStar(require("./endpoint"), exports);
@@ -0,0 +1,2 @@
1
+ import { NormalizedEvent } from '../types/index.js';
2
+ export declare const normalizeEvent: (event: any, type: string) => NormalizedEvent;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeEvent = void 0;
4
+ const parseQueryString = (queryString) => {
5
+ const params = {};
6
+ if (!queryString)
7
+ return params;
8
+ new URLSearchParams(queryString).forEach((value, key) => {
9
+ params[key] = value;
10
+ });
11
+ return params;
12
+ };
13
+ const normalizeEvent = (event, type) => {
14
+ const base = {
15
+ headers: event.headers || {},
16
+ body: event.body || null,
17
+ isBase64Encoded: event.isBase64Encoded || false,
18
+ requestContext: event.requestContext,
19
+ };
20
+ switch (type) {
21
+ case 'rest':
22
+ return {
23
+ ...base,
24
+ httpMethod: event.httpMethod,
25
+ path: event.path,
26
+ queryStringParameters: event.queryStringParameters || {},
27
+ multiValueQueryStringParameters: event.multiValueQueryStringParameters,
28
+ pathParameters: event.pathParameters || {},
29
+ cookies: event.headers?.Cookie ? [event.headers.Cookie] : [],
30
+ };
31
+ case 'http':
32
+ return {
33
+ ...base,
34
+ httpMethod: event.requestContext?.http?.method || 'GET',
35
+ path: event.rawPath,
36
+ queryStringParameters: event.queryStringParameters || {},
37
+ pathParameters: event.pathParameters || {},
38
+ cookies: event.cookies || [],
39
+ };
40
+ case 'url':
41
+ return {
42
+ ...base,
43
+ httpMethod: event.requestContext?.http?.method || 'GET',
44
+ path: event.rawPath,
45
+ queryStringParameters: parseQueryString(event.rawQueryString),
46
+ pathParameters: {},
47
+ cookies: [],
48
+ };
49
+ default:
50
+ throw new Error(`Unsupported event type: ${type}`);
51
+ }
52
+ };
53
+ exports.normalizeEvent = normalizeEvent;
@@ -12,4 +12,5 @@ export declare class MultipartProcessor {
12
12
  files: Record<string, MultipartFile | MultipartFile[]>;
13
13
  };
14
14
  static isMultipart(request: any): boolean;
15
+ private static getContentType;
15
16
  }
@@ -69,10 +69,11 @@ class MultipartProcessor {
69
69
  parts.forEach((part) => {
70
70
  if (part.filename) {
71
71
  const fieldName = part.name || 'file';
72
+ const contentType = this.getContentType(part);
72
73
  const fileData = {
73
74
  fieldname: fieldName,
74
75
  filename: part.filename,
75
- contentType: part.type,
76
+ contentType: contentType ?? part.type,
76
77
  data: part.data,
77
78
  size: part.data.length,
78
79
  encoding: part.encoding,
@@ -105,5 +106,101 @@ class MultipartProcessor {
105
106
  const contentType = request.headers?.['content-type'] || request.headers?.['Content-Type'] || '';
106
107
  return contentType.startsWith('multipart/form-data');
107
108
  }
109
+ static getContentType(part) {
110
+ const filename = part.filename || '';
111
+ const extension = filename.split('.').pop()?.toLowerCase() ?? '';
112
+ const mimeMap = {
113
+ txt: 'text/plain',
114
+ text: 'text/plain',
115
+ log: 'text/plain',
116
+ md: 'text/markdown',
117
+ csv: 'text/csv',
118
+ ts: 'application/typescript',
119
+ tsx: 'application/typescript',
120
+ js: 'application/javascript',
121
+ jsx: 'application/javascript',
122
+ mjs: 'application/javascript',
123
+ cjs: 'application/javascript',
124
+ json: 'application/json',
125
+ xml: 'application/xml',
126
+ yaml: 'application/yaml',
127
+ yml: 'application/yaml',
128
+ toml: 'application/toml',
129
+ php: 'application/x-httpd-php',
130
+ py: 'text/x-python',
131
+ rb: 'text/x-ruby',
132
+ java: 'text/x-java',
133
+ c: 'text/x-c',
134
+ cpp: 'text/x-c++',
135
+ h: 'text/x-c',
136
+ hpp: 'text/x-c++',
137
+ go: 'text/x-go',
138
+ rs: 'text/x-rust',
139
+ swift: 'text/x-swift',
140
+ kt: 'text/x-kotlin',
141
+ kts: 'text/x-kotlin',
142
+ html: 'text/html',
143
+ htm: 'text/html',
144
+ css: 'text/css',
145
+ scss: 'text/x-scss',
146
+ sass: 'text/x-sass',
147
+ less: 'text/x-less',
148
+ jpg: 'image/jpeg',
149
+ jpeg: 'image/jpeg',
150
+ png: 'image/png',
151
+ gif: 'image/gif',
152
+ webp: 'image/webp',
153
+ svg: 'image/svg+xml',
154
+ ico: 'image/x-icon',
155
+ bmp: 'image/bmp',
156
+ tiff: 'image/tiff',
157
+ tif: 'image/tiff',
158
+ avif: 'image/avif',
159
+ heic: 'image/heic',
160
+ heif: 'image/heif',
161
+ pdf: 'application/pdf',
162
+ doc: 'application/msword',
163
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
164
+ xls: 'application/vnd.ms-excel',
165
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
166
+ ppt: 'application/vnd.ms-powerpoint',
167
+ pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
168
+ odt: 'application/vnd.oasis.opendocument.text',
169
+ ods: 'application/vnd.oasis.opendocument.spreadsheet',
170
+ odp: 'application/vnd.oasis.opendocument.presentation',
171
+ zip: 'application/zip',
172
+ rar: 'application/vnd.rar',
173
+ '7z': 'application/x-7z-compressed',
174
+ tar: 'application/x-tar',
175
+ gz: 'application/gzip',
176
+ bz2: 'application/x-bzip2',
177
+ mp3: 'audio/mpeg',
178
+ wav: 'audio/wav',
179
+ ogg: 'audio/ogg',
180
+ m4a: 'audio/mp4',
181
+ flac: 'audio/flac',
182
+ aac: 'audio/aac',
183
+ mp4: 'video/mp4',
184
+ mpg: 'video/mpeg',
185
+ mpeg: 'video/mpeg',
186
+ mov: 'video/quicktime',
187
+ avi: 'video/x-msvideo',
188
+ wmv: 'video/x-ms-wmv',
189
+ flv: 'video/x-flv',
190
+ webm: 'video/webm',
191
+ mkv: 'video/x-matroska',
192
+ m4v: 'video/x-m4v',
193
+ bin: 'application/octet-stream',
194
+ exe: 'application/vnd.microsoft.portable-executable',
195
+ dll: 'application/vnd.microsoft.portable-executable',
196
+ deb: 'application/vnd.debian.binary-package',
197
+ rpm: 'application/x-rpm',
198
+ iso: 'application/x-iso9660-image',
199
+ sh: 'application/x-sh',
200
+ bat: 'application/x-msdos-program',
201
+ ps1: 'application/x-powershell',
202
+ };
203
+ return mimeMap[extension];
204
+ }
108
205
  }
109
206
  exports.MultipartProcessor = MultipartProcessor;
@@ -15,6 +15,7 @@ const resolveConfig = (configOrClass) => {
15
15
  ...decoratorConfig,
16
16
  errorHandler: decoratorConfig.errorHandler ?? errorHandler,
17
17
  interceptors,
18
+ cors: decoratorConfig.cors,
18
19
  controllers: [...controllers, ...(decoratorConfig.controllers || [])],
19
20
  };
20
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quantum-flow",
3
- "version": "1.3.9",
3
+ "version": "1.4.0",
4
4
  "description": "Decorator-based API framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",