@expressots/adapter-express 1.7.0 → 1.8.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/lib/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
 
2
2
 
3
+ ## [1.8.0](https://github.com/expressots/adapter-express/compare/1.7.0...1.8.0) (2024-08-13)
4
+
5
+
6
+ ### Features
7
+
8
+ * add expressots middleware in decorators ([055ead9](https://github.com/expressots/adapter-express/commit/055ead94334dd5ea735ddf04a16a322d56d566cc))
9
+ * add FileUpload decorator ([a4f62b1](https://github.com/expressots/adapter-express/commit/a4f62b1cd377625c55218069599a7a262493e3f5))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * adjust error msgs and package resolver ([34a0fcd](https://github.com/expressots/adapter-express/commit/34a0fcde6e637130e328733f48130408fa881a67))
15
+ * adjust isExpressoMiddleware function param types ([edc0888](https://github.com/expressots/adapter-express/commit/edc0888489df4c9f33a5b97af0820ce8f90904e9))
16
+ * return message ([32d656e](https://github.com/expressots/adapter-express/commit/32d656ec2b05a807599e428c9bd0389849c2165d))
17
+
18
+
19
+ ### Code Refactoring
20
+
21
+ * bump expresso core deps & remove husky deprecated code ([a9b243f](https://github.com/expressots/adapter-express/commit/a9b243fd35b877c839da2871401574cf30db808e))
22
+ * httpMethod to Method decorator ([5e853a0](https://github.com/expressots/adapter-express/commit/5e853a04358430e9e6f658bf0d0b68b7e2708a24))
23
+ * remove unnecessary comments ([72c4a15](https://github.com/expressots/adapter-express/commit/72c4a157055fffdfbaad19a69ea124ca0eb7fa13))
24
+
3
25
  ## [1.7.0](https://github.com/expressots/adapter-express/compare/1.6.0...1.7.0) (2024-07-23)
4
26
 
5
27
 
@@ -10,13 +10,16 @@ exports.Put = Put;
10
10
  exports.Patch = Patch;
11
11
  exports.Head = Head;
12
12
  exports.Delete = Delete;
13
- exports.httpMethod = httpMethod;
13
+ exports.Method = Method;
14
14
  exports.params = params;
15
15
  exports.Render = Render;
16
16
  exports.getRenderMetadata = getRenderMetadata;
17
+ exports.FileUpload = FileUpload;
17
18
  require("reflect-metadata");
18
19
  const inversify_1 = require("inversify");
19
20
  const constants_1 = require("./constants");
21
+ const resolver_multer_1 = require("./resolver-multer");
22
+ const core_1 = require("@expressots/core");
20
23
  exports.injectHttpContext = (0, inversify_1.inject)(constants_1.TYPE.HttpContext);
21
24
  /**
22
25
  * Controller decorator to define a new controller
@@ -83,7 +86,7 @@ function Http(code) {
83
86
  * @param middleware array of middleware to be applied to all routes defined in path logic
84
87
  */
85
88
  function All(path, ...middleware) {
86
- return httpMethod("all", path, ...middleware);
89
+ return Method("all", path, ...middleware);
87
90
  }
88
91
  /**
89
92
  * Decorator to allow GET HTTP method
@@ -99,7 +102,7 @@ function Get(path, ...middleware) {
99
102
  * @param middleware array of middleware to be applied to the route
100
103
  */
101
104
  function Post(path, ...middleware) {
102
- return httpMethod("post", path, ...middleware);
105
+ return Method("post", path, ...middleware);
103
106
  }
104
107
  /**
105
108
  * Decorator to allow PUT HTTP method
@@ -123,7 +126,7 @@ function Patch(path, ...middleware) {
123
126
  * @param middleware array of middleware to be applied to the route
124
127
  */
125
128
  function Head(path, ...middleware) {
126
- return httpMethod("head", path, ...middleware);
129
+ return Method("head", path, ...middleware);
127
130
  }
128
131
  /**
129
132
  * Decorator to allow DELETE HTTP method
@@ -189,7 +192,7 @@ function enhancedHttpMethod(method, path, ...middleware) {
189
192
  * @param path route path
190
193
  * @param middleware array of middleware to be applied to the route
191
194
  */
192
- function httpMethod(method, path, ...middleware) {
195
+ function Method(method, path, ...middleware) {
193
196
  return (target, key) => {
194
197
  const metadata = {
195
198
  key,
@@ -336,3 +339,87 @@ function convertToType(value, type) {
336
339
  }
337
340
  return value;
338
341
  }
342
+ /**
343
+ * File upload decorator to handle file uploads
344
+ * @param options
345
+ * @param multerOptions
346
+ * @default { none: true }
347
+ * @returns MethodDecorator
348
+ */
349
+ function FileUpload(options, multerOptions) {
350
+ const multer = (0, resolver_multer_1.packageResolver)("multer");
351
+ /* eslint-disable @typescript-eslint/no-explicit-any */
352
+ let upload;
353
+ let method = "none";
354
+ if (multer) {
355
+ if (options === undefined) {
356
+ options = { none: true };
357
+ }
358
+ upload = multer(multerOptions);
359
+ method = inferMulterMethod(options);
360
+ }
361
+ return function (target, propertyKey, descriptor) {
362
+ const originalMethod = descriptor.value;
363
+ /* eslint-disable @typescript-eslint/no-explicit-any */
364
+ descriptor.value = function (...args) {
365
+ const req = args[0];
366
+ const res = args[1];
367
+ // const next = args[2] as NextFunction;
368
+ const multerMiddleware = getMulterMiddleware(upload, options, method);
369
+ multerMiddleware(req, res, (err) => {
370
+ if (err) {
371
+ return res.status(400).json({ error: err.message });
372
+ }
373
+ return originalMethod.apply(this, args);
374
+ });
375
+ };
376
+ };
377
+ }
378
+ /**
379
+ * Infer the multer method to use based on the provided options
380
+ * @param options
381
+ * @returns
382
+ */
383
+ function inferMulterMethod(options) {
384
+ const report = new core_1.Report();
385
+ if (options.none)
386
+ return "none";
387
+ if (options.any)
388
+ return "any";
389
+ if (Array.isArray(options))
390
+ return "fields";
391
+ if (options.fieldName && options.maxCount !== undefined)
392
+ return "array";
393
+ if (options.fieldName)
394
+ return "single";
395
+ throw report.error("Invalid options provided for FileUpload.", core_1.StatusCode.InternalServerError, "multer-decorator");
396
+ }
397
+ /**
398
+ * Get the multer middleware based on the method
399
+ * @param upload
400
+ * @param options
401
+ * @param method
402
+ * @returns
403
+ */
404
+ function getMulterMiddleware(upload, options, method) {
405
+ const report = new core_1.Report();
406
+ switch (method) {
407
+ case "single":
408
+ return upload.single(options.fieldName);
409
+ case "array":
410
+ return upload.array(options.fieldName, options.maxCount);
411
+ case "fields": {
412
+ const fieldsOptions = options.map((opt) => ({
413
+ name: opt.fieldName,
414
+ maxCount: opt.maxCount,
415
+ }));
416
+ return upload.fields(fieldsOptions);
417
+ }
418
+ case "none":
419
+ return upload.none();
420
+ case "any":
421
+ return upload.any();
422
+ default:
423
+ throw report.error(`Unsupported Multer method: ${method}`, core_1.StatusCode.InternalServerError, "multer-decorator");
424
+ }
425
+ }
@@ -126,22 +126,32 @@ class InversifyExpressServer {
126
126
  const methodMetadata = (0, utils_1.getControllerMethodMetadata)(controller.constructor);
127
127
  const parameterMetadata = (0, utils_1.getControllerParameterMetadata)(controller.constructor);
128
128
  if (controllerMetadata && methodMetadata) {
129
- const controllerMiddleware = this.resolveMidleware(...controllerMetadata.middleware);
129
+ const controllerMiddleware = this.resolveMiddleware(...controllerMetadata.middleware);
130
130
  methodMetadata.forEach((metadata) => {
131
131
  let paramList = [];
132
132
  if (parameterMetadata) {
133
133
  paramList = parameterMetadata[metadata.key] || [];
134
134
  }
135
135
  const handler = this.handlerFactory(controllerMetadata.target.name, metadata.key, paramList);
136
- const routeMiddleware = this.resolveMidleware(...metadata.middleware);
136
+ const routeMiddleware = this.resolveMiddleware(...metadata.middleware);
137
137
  this._router[metadata.method](`${controllerMetadata.path}${metadata.path}`, ...controllerMiddleware, ...routeMiddleware, handler);
138
138
  });
139
139
  }
140
140
  });
141
141
  this._app.use(this._routingConfig.rootPath, this._router);
142
142
  }
143
- resolveMidleware(...middleware) {
143
+ isExpressoMiddleware(middlewareItem) {
144
+ return (typeof middlewareItem === "object" &&
145
+ "use" in middlewareItem &&
146
+ typeof middlewareItem.use === "function");
147
+ }
148
+ resolveMiddleware(...middleware) {
144
149
  return middleware.map((middlewareItem) => {
150
+ if (this.isExpressoMiddleware(middlewareItem)) {
151
+ return (req, res, next) => {
152
+ middlewareItem.use(req, res, next);
153
+ };
154
+ }
145
155
  if (!this._container.isBound(middlewareItem)) {
146
156
  return middlewareItem;
147
157
  }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.packageResolver = packageResolver;
4
+ /* eslint-disable @typescript-eslint/no-explicit-any */
5
+ const core_1 = require("@expressots/core");
6
+ /**
7
+ * Resolve package from the current working directory.
8
+ * @param packageName
9
+ * @param options
10
+ * @returns
11
+ */
12
+ function packageResolver(packageName) {
13
+ const logger = new core_1.Logger();
14
+ try {
15
+ const hasPackage = require.resolve(packageName, {
16
+ paths: [process.cwd()],
17
+ });
18
+ if (hasPackage) {
19
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
20
+ const packageResolved = require(hasPackage);
21
+ if (typeof packageResolved === "function") {
22
+ return packageResolved;
23
+ }
24
+ if (packageResolved.default && typeof packageResolved.default === "function") {
25
+ return packageResolved.default;
26
+ }
27
+ return packageResolved;
28
+ }
29
+ }
30
+ catch (error) {
31
+ logger.warn(`Package [${packageName}] not installed. Please install it using your package manager.`, "package-resolver");
32
+ }
33
+ }
@@ -70,7 +70,7 @@ export declare function Delete(path: string, ...middleware: Array<Middleware>):
70
70
  * @param path route path
71
71
  * @param middleware array of middleware to be applied to the route
72
72
  */
73
- export declare function httpMethod(method: keyof typeof HTTP_VERBS_ENUM, path: string, ...middleware: Array<Middleware>): HandlerDecorator;
73
+ export declare function Method(method: keyof typeof HTTP_VERBS_ENUM, path: string, ...middleware: Array<Middleware>): HandlerDecorator;
74
74
  /**
75
75
  * Parameter decorator to inject the request object
76
76
  * @returns ParameterDecorator
@@ -132,3 +132,51 @@ export declare function getRenderMetadata(target: object, propertyKey: string |
132
132
  template?: string;
133
133
  defaultData?: Record<string, unknown>;
134
134
  };
135
+ export interface StorageEngine {
136
+ _handleFile(req: unknown, file: MulterFile, callback: (error?: Error | null, info?: Partial<MulterFile>) => void): void;
137
+ _removeFile(req: unknown, file: MulterFile, callback: (error: Error | null) => void): void;
138
+ }
139
+ export interface MulterFile {
140
+ fieldname: string;
141
+ originalname: string;
142
+ encoding: string;
143
+ mimetype: string;
144
+ size: number;
145
+ destination?: string;
146
+ filename?: string;
147
+ path?: string;
148
+ buffer?: Buffer;
149
+ }
150
+ export interface MulterLimits {
151
+ fieldNameSize?: number;
152
+ fieldSize?: number;
153
+ fields?: number;
154
+ fileSize?: number;
155
+ files?: number;
156
+ parts?: number;
157
+ headerPairs?: number;
158
+ }
159
+ export interface MulterOptions {
160
+ dest?: string;
161
+ storage?: StorageEngine;
162
+ limits?: MulterLimits;
163
+ fileFilter?: FileFilter;
164
+ }
165
+ export type FileFilterCallback = (error: Error | null, acceptFile: boolean) => void;
166
+ export type FileFilter = (req: unknown, file: MulterFile, callback: FileFilterCallback) => void;
167
+ type FieldOptions = {
168
+ fieldName: string;
169
+ maxCount?: number;
170
+ };
171
+ /**
172
+ * File upload decorator to handle file uploads
173
+ * @param options
174
+ * @param multerOptions
175
+ * @default { none: true }
176
+ * @returns MethodDecorator
177
+ */
178
+ export declare function FileUpload(options?: FieldOptions | Array<FieldOptions> | {
179
+ none?: boolean;
180
+ any?: boolean;
181
+ }, multerOptions?: MulterOptions): MethodDecorator;
182
+ export {};
@@ -12,7 +12,10 @@ interface ConstructorFunction<T = Record<string, unknown>> {
12
12
  prototype: Prototype<T>;
13
13
  }
14
14
  export type DecoratorTarget<T = unknown> = ConstructorFunction<T> | Prototype<T>;
15
- export type Middleware = string | symbol | RequestHandler;
15
+ export interface IExpressoMiddleware {
16
+ use(req: Request, res: Response, next: NextFunction): Promise<void> | void;
17
+ }
18
+ export type Middleware = string | symbol | RequestHandler | IExpressoMiddleware;
16
19
  export type ControllerHandler = (...params: Array<unknown>) => unknown;
17
20
  export type BaseController = Record<string, ControllerHandler>;
18
21
  export interface Controller {
@@ -46,7 +46,8 @@ export declare class InversifyExpressServer {
46
46
  */
47
47
  build(): express.Application;
48
48
  private registerControllers;
49
- private resolveMidleware;
49
+ private isExpressoMiddleware;
50
+ private resolveMiddleware;
50
51
  private copyHeadersTo;
51
52
  private handleHttpResponseMessage;
52
53
  private handlerFactory;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Resolve package from the current working directory.
3
+ * @param packageName
4
+ * @param options
5
+ * @returns
6
+ */
7
+ export declare function packageResolver(packageName: string): any;
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expressots/adapter-express",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Expressots - modern, fast, lightweight nodejs web framework (@adapter-express)",
5
5
  "author": "",
6
6
  "main": "./lib/cjs/index.js",
@@ -75,7 +75,7 @@
75
75
  "@codecov/vite-plugin": "^0.0.1-beta.6",
76
76
  "@commitlint/cli": "19.2.1",
77
77
  "@commitlint/config-conventional": "19.2.2",
78
- "@expressots/core": "2.15.0",
78
+ "@expressots/core": "2.16.0",
79
79
  "@release-it/conventional-changelog": "8.0.1",
80
80
  "@types/express": "4.17.21",
81
81
  "@types/node": "20.14.10",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expressots/adapter-express",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Expressots - modern, fast, lightweight nodejs web framework (@adapter-express)",
5
5
  "author": "",
6
6
  "main": "./lib/cjs/index.js",
@@ -75,7 +75,7 @@
75
75
  "@codecov/vite-plugin": "^0.0.1-beta.6",
76
76
  "@commitlint/cli": "19.2.1",
77
77
  "@commitlint/config-conventional": "19.2.2",
78
- "@expressots/core": "2.15.0",
78
+ "@expressots/core": "2.16.0",
79
79
  "@release-it/conventional-changelog": "8.0.1",
80
80
  "@types/express": "4.17.21",
81
81
  "@types/node": "20.14.10",