@avleon/core 0.0.24 → 0.0.25

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.
@@ -1,5 +1,26 @@
1
1
  import { Constructor } from "./helpers";
2
2
  import { RouteShorthandMethod } from "fastify";
3
+ export interface AvleonApplication {
4
+ useCors: () => void;
5
+ useOpenApi: () => void;
6
+ useView: () => void;
7
+ useAuth: () => void;
8
+ useMultipart: () => void;
9
+ useDataSource: () => void;
10
+ useMiddlewares: () => void;
11
+ useControllers: () => void;
12
+ useAutoControllers: () => void;
13
+ useStaticFiles: () => void;
14
+ useCustomErrorHandler: () => void;
15
+ mapGroup: () => any;
16
+ mapGet: () => any;
17
+ mapPost: () => any;
18
+ mapPut: () => any;
19
+ mapPatch: () => any;
20
+ mapDelete: () => any;
21
+ mapView: () => any;
22
+ run: (port: number) => void;
23
+ }
3
24
  export interface InlineRoutes {
4
25
  get: RouteShorthandMethod;
5
26
  post: RouteShorthandMethod;
@@ -0,0 +1,12 @@
1
+ import type { Redis } from 'ioredis';
2
+ export declare class CacheManager {
3
+ private store;
4
+ private tagsMap;
5
+ private redis;
6
+ constructor(redisInstance?: Redis);
7
+ private redisTagKey;
8
+ get<T>(key: string): Promise<T | null>;
9
+ set<T>(key: string, value: T, tags?: string[], ttl?: number): Promise<void>;
10
+ delete(key: string): Promise<void>;
11
+ invalidateTag(tag: string): Promise<void>;
12
+ }
package/dist/cache.js ADDED
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CacheManager = void 0;
4
+ class CacheManager {
5
+ constructor(redisInstance) {
6
+ this.store = new Map();
7
+ this.tagsMap = new Map();
8
+ this.redis = null;
9
+ this.redis = redisInstance || null;
10
+ }
11
+ redisTagKey(tag) {
12
+ return `cache-tags:${tag}`;
13
+ }
14
+ async get(key) {
15
+ if (this.redis) {
16
+ const val = await this.redis.get(key);
17
+ return val ? JSON.parse(val) : null;
18
+ }
19
+ const cached = this.store.get(key);
20
+ return cached ? cached.data : null;
21
+ }
22
+ async set(key, value, tags = [], ttl = 3600) {
23
+ const entry = {
24
+ data: value,
25
+ timestamp: Date.now(),
26
+ };
27
+ if (this.redis) {
28
+ await this.redis.set(key, JSON.stringify(entry.data), 'EX', ttl);
29
+ for (const tag of tags) {
30
+ await this.redis.sadd(this.redisTagKey(tag), key);
31
+ }
32
+ }
33
+ else {
34
+ this.store.set(key, entry);
35
+ for (const tag of tags) {
36
+ if (!this.tagsMap.has(tag))
37
+ this.tagsMap.set(tag, new Set());
38
+ this.tagsMap.get(tag).add(key);
39
+ }
40
+ }
41
+ }
42
+ async delete(key) {
43
+ if (this.redis) {
44
+ await this.redis.del(key);
45
+ // Also clean up from any tag sets
46
+ const tagKeys = await this.redis.keys('cache-tags:*');
47
+ for (const tagKey of tagKeys) {
48
+ await this.redis.srem(tagKey, key);
49
+ }
50
+ }
51
+ else {
52
+ this.store.delete(key);
53
+ for (const keys of this.tagsMap.values()) {
54
+ keys.delete(key);
55
+ }
56
+ }
57
+ }
58
+ async invalidateTag(tag) {
59
+ if (this.redis) {
60
+ const tagKey = this.redisTagKey(tag);
61
+ const keys = await this.redis.smembers(tagKey);
62
+ if (keys.length) {
63
+ await this.redis.del(...keys); // delete all cached keys
64
+ await this.redis.del(tagKey); // delete the tag set
65
+ }
66
+ }
67
+ else {
68
+ const keys = this.tagsMap.get(tag);
69
+ if (keys) {
70
+ for (const key of keys) {
71
+ this.store.delete(key);
72
+ }
73
+ this.tagsMap.delete(tag);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ exports.CacheManager = CacheManager;
package/dist/icore.d.ts CHANGED
@@ -8,7 +8,7 @@ import { FastifyReply, FastifyRequest, HookHandlerDoneFunction, InjectOptions, L
8
8
  import { Constructable } from "typedi";
9
9
  import { Constructor } from "./helpers";
10
10
  import { PathLike } from "fs";
11
- import { DataSourceOptions } from "typeorm";
11
+ import { DataSource, DataSourceOptions } from "typeorm";
12
12
  import { AppMiddleware } from "./middleware";
13
13
  import { OpenApiOptions, OpenApiUiOptions } from "./openapi";
14
14
  import { IConfig } from "./config";
@@ -78,19 +78,24 @@ type MultipartOptions = {
78
78
  } & FastifyMultipartOptions;
79
79
  export type TestAppOptions = {
80
80
  controllers: Constructor[];
81
+ dataSource?: DataSource;
81
82
  };
82
83
  export interface AvleonTestAppliction {
83
84
  addDataSource: (dataSourceOptions: DataSourceOptions) => void;
84
85
  getApp: (options?: TestAppOptions) => any;
85
- getController: <T>(controller: Constructor<T>) => T;
86
+ getController: <T>(controller: Constructor<T>, deps: any[]) => T;
86
87
  }
88
+ export type AutoControllerOptions = {
89
+ auto: true;
90
+ path?: string;
91
+ };
87
92
  export interface IAvleonApplication {
88
93
  isDevelopment(): boolean;
89
94
  useCors(corsOptions?: FastifyCorsOptions): void;
90
95
  useDataSource<T extends IConfig<R>, R = ReturnType<InstanceType<Constructable<T>>["config"]>>(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R): void;
91
96
  useOpenApi<T extends IConfig<R>, R = ReturnType<InstanceType<Constructable<T>>["config"]>>(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R): void;
92
- useSwagger(options: OpenApiUiOptions): Promise<void>;
93
97
  useMultipart(options: MultipartOptions): void;
98
+ useCache(options: any): void;
94
99
  useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]): void;
95
100
  useAuthoriztion<T extends any>(middleware: Constructor<T>): void;
96
101
  mapRoute<T extends (...args: any[]) => any>(method: "get" | "post" | "put" | "delete", path: string, fn: T): Promise<void>;
@@ -98,11 +103,15 @@ export interface IAvleonApplication {
98
103
  mapPost<T extends (...args: any[]) => any>(path: string, fn: T): any;
99
104
  mapPut<T extends (...args: any[]) => any>(path: string, fn: T): any;
100
105
  mapDelete<T extends (...args: any[]) => any>(path: string, fn: T): any;
101
- mapControllers(controllers: any[]): any;
106
+ useControllers(controllers: any[]): any;
107
+ useControllers(controllersOptions: AutoControllerOptions): any;
108
+ useControllers(controllersOrOptions: any[] | AutoControllerOptions): any;
102
109
  useStaticFiles(options?: StaticFileOptions): void;
103
110
  run(port?: number): Promise<void>;
104
111
  getTestApp(): TestApplication;
105
112
  }
113
+ type OpenApiConfigClass<T = any> = Constructable<IConfig<T>>;
114
+ type OpenApiConfigInput<T = any> = OpenApiConfigClass<T> | T;
106
115
  export declare class AvleonApplication {
107
116
  private static instance;
108
117
  private static buildOptions;
@@ -120,6 +129,8 @@ export declare class AvleonApplication {
120
129
  private appConfig;
121
130
  private dataSource?;
122
131
  private isMapFeatures;
132
+ private registerControllerAuto;
133
+ private registerControllerPath;
123
134
  private metaCache;
124
135
  private multipartOptions;
125
136
  private constructor();
@@ -129,7 +140,7 @@ export declare class AvleonApplication {
129
140
  isDevelopment(): boolean;
130
141
  private initSwagger;
131
142
  useCors(corsOptions?: FastifyCorsOptions): void;
132
- useOpenApi<T extends IConfig<R>, R = ReturnType<InstanceType<Constructable<T>>["config"]>>(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R): void;
143
+ useOpenApi<T = OpenApiUiOptions>(configOrClass: OpenApiConfigInput<T>, modifyConfig?: (config: T) => T): void;
133
144
  /**
134
145
  * Registers the fastify-multipart plugin with the Fastify instance.
135
146
  * This enables handling of multipart/form-data requests, typically used for file uploads.
@@ -155,6 +166,7 @@ export declare class AvleonApplication {
155
166
  * @see {@link https://typeorm.io/} for more information about TypeORM.
156
167
  */
157
168
  useDataSource<T extends IConfig<R>, R = ReturnType<InstanceType<Constructable<T>>["config"]>>(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R): void;
169
+ useCache(options: any): void;
158
170
  /**
159
171
  * Registers an array of middleware classes to be executed before request handlers.
160
172
  * It retrieves instances of the middleware classes from the dependency injection container
@@ -210,12 +222,10 @@ export declare class AvleonApplication {
210
222
  * @returns
211
223
  */
212
224
  private _processMeta;
213
- autoControllers(): Promise<void>;
214
- mapControllers(controllers: Function[]): void;
215
- mapFeature(): void;
225
+ private _resolveControllerDir;
226
+ private autoControllers;
227
+ useControllers(controllers: Constructor[] | AutoControllerOptions): void;
216
228
  private _mapControllers;
217
- mapControllersAuto(): void;
218
- handleRoute(args: any): Promise<void>;
219
229
  private mapFn;
220
230
  private _handleError;
221
231
  mapRoute<T extends (...args: any[]) => any>(method: "get" | "post" | "put" | "delete", path: string | undefined, fn: T): Promise<void>;
@@ -251,20 +261,19 @@ export interface IAppBuilder {
251
261
  addDataSource<T extends IConfig<R>, R = ReturnType<InstanceType<Constructable<T>>["config"]>>(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R): void;
252
262
  build<T extends IAvleonApplication>(): T;
253
263
  }
254
- export declare class TestBuilder {
264
+ export declare class AvleonTest {
255
265
  private static instance;
256
266
  private app;
257
267
  private dataSourceOptions?;
258
268
  private constructor();
259
- static createBuilder(): TestBuilder;
260
- addDatasource(options: DataSourceOptions): void;
261
- private getController;
269
+ private addDatasource;
270
+ getController<T>(controller: Constructor<T>, deps?: any[]): T;
262
271
  private getService;
263
- getTestApplication(options: TestAppOptions): TestApplication;
264
- build(app: IAvleonApplication): TestApplication;
265
- fromApplication(app: IAvleonApplication): TestApplication;
272
+ static createTestApplication(options: TestAppOptions): TestApplication;
273
+ static from(app: AvleonApplication): TestApplication;
274
+ static clean(): void;
266
275
  }
267
- export declare class Builder {
276
+ export declare class Avleon {
268
277
  static createApplication(): AvleonApplication;
269
278
  }
270
279
  export {};
package/dist/icore.js CHANGED
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Builder = exports.TestBuilder = exports.AvleonApplication = void 0;
39
+ exports.Avleon = exports.AvleonTest = exports.AvleonApplication = void 0;
40
40
  /**
41
41
  * @copyright 2024
42
42
  * @author Tareq Hossain
@@ -50,7 +50,6 @@ const path_1 = __importDefault(require("path"));
50
50
  const container_1 = __importStar(require("./container"));
51
51
  const helpers_1 = require("./helpers");
52
52
  const system_exception_1 = require("./exceptions/system-exception");
53
- const fs_1 = require("fs");
54
53
  const exceptions_1 = require("./exceptions");
55
54
  const swagger_1 = __importDefault(require("@fastify/swagger"));
56
55
  const config_1 = require("./config");
@@ -78,6 +77,8 @@ class AvleonApplication {
78
77
  this.authorizeMiddleware = undefined;
79
78
  this.dataSource = undefined;
80
79
  this.isMapFeatures = false;
80
+ this.registerControllerAuto = false;
81
+ this.registerControllerPath = './src';
81
82
  this.metaCache = new Map();
82
83
  this.app = (0, fastify_1.default)();
83
84
  this.appConfig = new config_1.AppConfig();
@@ -153,11 +154,24 @@ class AvleonApplication {
153
154
  useCors(corsOptions = {}) {
154
155
  this.app.register(cors_1.default, corsOptions);
155
156
  }
156
- useOpenApi(ConfigClass, modifyConfig) {
157
- const openApiConfig = this.appConfig.get(ConfigClass);
157
+ useOpenApi(configOrClass, modifyConfig) {
158
+ let openApiConfig;
159
+ const isClass = (input) => {
160
+ var _a;
161
+ return (typeof input === 'function' &&
162
+ typeof input.prototype === 'object' &&
163
+ ((_a = input.prototype) === null || _a === void 0 ? void 0 : _a.constructor) === input);
164
+ };
165
+ if (isClass(configOrClass)) {
166
+ // It's a class constructor
167
+ openApiConfig = this.appConfig.get(configOrClass);
168
+ }
169
+ else {
170
+ // It's a plain object
171
+ openApiConfig = configOrClass;
172
+ }
158
173
  if (modifyConfig) {
159
- const modifiedConfig = modifyConfig(openApiConfig);
160
- this.globalSwaggerOptions = modifiedConfig;
174
+ this.globalSwaggerOptions = modifyConfig(openApiConfig);
161
175
  }
162
176
  else {
163
177
  this.globalSwaggerOptions = openApiConfig;
@@ -208,6 +222,8 @@ class AvleonApplication {
208
222
  this.dataSource = datasource;
209
223
  typedi_1.default.set("idatasource", datasource);
210
224
  }
225
+ useCache(options) {
226
+ }
211
227
  /**
212
228
  * Registers an array of middleware classes to be executed before request handlers.
213
229
  * It retrieves instances of the middleware classes from the dependency injection container
@@ -463,32 +479,59 @@ class AvleonApplication {
463
479
  this.metaCache.set(cacheKey, meta);
464
480
  return meta;
465
481
  }
466
- async autoControllers() {
467
- const controllers = [];
468
- const files = await promises_1.default.readdir(controllerDir);
482
+ _resolveControllerDir(dir) {
483
+ const isTsNode = process.env.TS_NODE_DEV ||
484
+ process.env.TS_NODE_PROJECT ||
485
+ process[Symbol.for("ts-node.register.instance")];
486
+ const controllerDir = path_1.default.join(process.cwd(), this.registerControllerPath);
487
+ return isTsNode ? controllerDir : controllerDir.replace('src', 'dist');
488
+ }
489
+ async autoControllers(controllersPath) {
490
+ const conDir = this._resolveControllerDir(controllersPath);
491
+ const files = await promises_1.default.readdir(conDir, { recursive: true });
469
492
  for (const file of files) {
493
+ const isTestFile = /\.(test|spec|e2e-spec)\.ts$/.test(file);
494
+ if (isTestFile)
495
+ continue;
470
496
  if (isTsNode ? file.endsWith(".ts") : file.endsWith(".js")) {
471
- const filePath = path_1.default.join(controllerDir, file);
497
+ const filePath = path_1.default.join(conDir, file);
472
498
  const module = await Promise.resolve(`${filePath}`).then(s => __importStar(require(s)));
473
499
  for (const exported of Object.values(module)) {
474
500
  if (typeof exported === "function" && (0, container_1.isApiController)(exported)) {
475
- this.buildController(exported);
501
+ console.log('adding', exported.name);
502
+ if (!this.controllers.some(con => exported.name == con.name)) {
503
+ this.controllers.push(exported);
504
+ }
505
+ //this.buildController(exported);
476
506
  }
477
507
  }
478
508
  }
479
509
  }
480
510
  }
481
- mapControllers(controllers) {
482
- this.controllers = controllers;
511
+ useControllers(controllers) {
512
+ if (Array.isArray(controllers)) {
513
+ this.controllers = controllers;
514
+ controllers.forEach(controller => {
515
+ if (!this.controllers.includes(controller)) {
516
+ this.controllers.push(controller);
517
+ }
518
+ });
519
+ }
520
+ else {
521
+ this.registerControllerAuto = true;
522
+ if (controllers.path) {
523
+ this.registerControllerPath = controllers.path;
524
+ }
525
+ }
483
526
  }
484
527
  // addFeature(feature:{controllers:Function[]}){
485
528
  // feature.controllers.forEach(c=> this.controllers.push(c))
486
529
  // }
487
- mapFeature() {
488
- if (!this.isMapFeatures) {
489
- this.isMapFeatures = true;
490
- }
491
- }
530
+ // mapFeature(){
531
+ // if(!this.isMapFeatures){
532
+ // this.isMapFeatures = true;
533
+ // }
534
+ // }
492
535
  async _mapControllers() {
493
536
  if (this.controllers.length > 0) {
494
537
  for (let controller of this.controllers) {
@@ -501,13 +544,10 @@ class AvleonApplication {
501
544
  }
502
545
  }
503
546
  }
504
- mapControllersAuto() {
505
- const isExists = (0, fs_1.existsSync)(controllerDir);
506
- if (isExists) {
507
- this.autoControllers();
508
- }
509
- }
510
- async handleRoute(args) { }
547
+ // useControllersAuto(controllerPath?:string) {
548
+ // this.registerControllerAuto = true;
549
+ // //this.autoControllers();
550
+ // }
511
551
  async mapFn(fn) {
512
552
  const original = fn;
513
553
  fn = function () { };
@@ -556,7 +596,7 @@ class AvleonApplication {
556
596
  middlewares: [],
557
597
  schema: {},
558
598
  });
559
- this.mapFn(fn);
599
+ // this.mapFn(fn);
560
600
  const route = {
561
601
  useMiddleware: (middlewares) => {
562
602
  const midds = Array.isArray(middlewares) ? middlewares : [middlewares];
@@ -613,6 +653,9 @@ class AvleonApplication {
613
653
  if (this.isMapFeatures) {
614
654
  this._mapFeatures();
615
655
  }
656
+ if (this.registerControllerAuto) {
657
+ await this.autoControllers();
658
+ }
616
659
  await this._mapControllers();
617
660
  this.rMap.forEach((value, key) => {
618
661
  const [m, r] = key.split(":");
@@ -680,7 +723,11 @@ class AvleonApplication {
680
723
  patch: async (url, options) => this.app.inject({ method: "PATCH", url, ...options }),
681
724
  delete: async (url, options) => this.app.inject({ method: "DELETE", url, ...options }),
682
725
  options: async (url, options) => this.app.inject({ method: "OPTIONS", url, ...options }),
683
- getController: (controller) => {
726
+ getController: (controller, deps = []) => {
727
+ const paramTypes = Reflect.getMetadata('design:paramtypes', controller) || [];
728
+ deps.forEach((dep, i) => {
729
+ typedi_1.default.set(paramTypes[i], dep);
730
+ });
684
731
  return typedi_1.default.get(controller);
685
732
  },
686
733
  };
@@ -693,44 +740,42 @@ class AvleonApplication {
693
740
  }
694
741
  exports.AvleonApplication = AvleonApplication;
695
742
  AvleonApplication.buildOptions = {};
696
- class TestBuilder {
743
+ class AvleonTest {
697
744
  constructor() {
698
745
  process.env.NODE_ENV = "test";
699
746
  }
700
- static createBuilder() {
701
- if (!TestBuilder.instance) {
702
- TestBuilder.instance = new TestBuilder();
703
- }
704
- return TestBuilder.instance;
705
- }
706
747
  addDatasource(options) {
707
748
  this.dataSourceOptions = options;
708
749
  }
709
- getController(controller) {
750
+ getController(controller, deps = []) {
751
+ const paramTypes = Reflect.getMetadata('design:paramtypes', controller) || [];
752
+ deps.forEach((dep, i) => {
753
+ typedi_1.default.set(paramTypes[i], dep);
754
+ });
710
755
  return typedi_1.default.get(controller);
711
756
  }
712
757
  getService(service) {
713
758
  return typedi_1.default.get(service);
714
759
  }
715
- getTestApplication(options) {
760
+ static createTestApplication(options) {
716
761
  const app = AvleonApplication.getInternalApp({
717
- dataSourceOptions: this.dataSourceOptions,
762
+ dataSourceOptions: options.dataSource ? options.dataSource : null,
718
763
  });
719
- app.mapControllers([...options.controllers]);
764
+ app.useControllers([...options.controllers]);
720
765
  return app.getTestApp();
721
766
  }
722
- build(app) {
767
+ static from(app) {
723
768
  return app.getTestApp();
724
769
  }
725
- fromApplication(app) {
726
- return app.getTestApp();
770
+ static clean() {
771
+ typedi_1.default.reset();
727
772
  }
728
773
  }
729
- exports.TestBuilder = TestBuilder;
730
- class Builder {
774
+ exports.AvleonTest = AvleonTest;
775
+ class Avleon {
731
776
  static createApplication() {
732
777
  const app = AvleonApplication.getApp();
733
778
  return app;
734
779
  }
735
780
  }
736
- exports.Builder = Builder;
781
+ exports.Avleon = Avleon;
package/dist/testing.js CHANGED
@@ -115,7 +115,7 @@ class AvleonTestBuilder {
115
115
  dataSourceOptions: this.testOptions.dataSourceOptions,
116
116
  });
117
117
  // Map controllers
118
- app.mapControllers(this.controllers);
118
+ app.useControllers(this.controllers);
119
119
  // Get test application
120
120
  return app.getTestApp();
121
121
  }
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@avleon/core",
3
- "version": "0.0.24",
3
+ "version": "0.0.25",
4
4
  "main": "./dist/index.js",
5
+ "types": ["./dist/icore.d.ts"],
5
6
  "scripts": {
6
7
  "build": "npm run clean && tsc",
7
- "clean": "rm -rf dist",
8
+ "clean": "rimraf dist",
8
9
  "watch": "tsc-watch",
9
10
  "test": "jest",
10
11
  "test:watch": "jest --watch"
11
12
  },
12
- "keywords": [],
13
+ "keywords": ["restapi", "avleon", "backend","fastify"],
13
14
  "author": "Tareq Hossain",
14
15
  "license": "ISC",
15
16
  "devDependencies": {
@@ -17,6 +18,7 @@
17
18
  "@types/node": "^20.17.25",
18
19
  "class-transformer": "^0.5.1",
19
20
  "class-validator": "^0.14.1",
21
+ "ioredis": "^5.6.1",
20
22
  "jest": "^29.7.0",
21
23
  "nodemon": "^3.1.7",
22
24
  "sharp": "^0.33.5",
@@ -42,15 +44,22 @@
42
44
  "reflect-metadata": "^0.2.2",
43
45
  "typedi": "^0.10.0"
44
46
  },
45
- "peerDependencies": {
46
- "@scalar/fastify-api-reference": "*"
47
- },
48
- "peerDependenciesMeta": {
49
- "@scalar/fastify-api-reference": {
50
- "optional": true
51
- }
52
- },
53
-
47
+ "peerDependencies": {
48
+ "@scalar/fastify-api-reference": "*",
49
+ "ioredis": "*",
50
+ "sharp": "*"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "@scalar/fastify-api-reference": {
54
+ "optional": true
55
+ },
56
+ "ioredis": {
57
+ "optional": true
58
+ },
59
+ "sharp": {
60
+ "optional": true
61
+ }
62
+ },
54
63
  "directories": {
55
64
  "test": "tests"
56
65
  },
@@ -2,7 +2,36 @@ import Container from "typedi";
2
2
  import { Constructor } from "./helpers";
3
3
  import fastify, { FastifyInstance, RouteShorthandMethod } from "fastify";
4
4
 
5
+ export interface AvleonApplication{
6
+ // all public
7
+ useCors: () => void
8
+ useOpenApi:() => void
9
+ useView:()=> void;
10
+ useAuth:() => void
11
+ useMultipart:()=> void
12
+ useDataSource: () => void
13
+ useMiddlewares: () => void
14
+ useControllers: () => void
15
+ useAutoControllers:() => void
16
+ useStaticFiles: () => void
17
+ useCustomErrorHandler: () => void
18
+
19
+
20
+
21
+
22
+ // all mapping
23
+ mapGroup:() => any;
24
+ mapGet:()=> any;
25
+ mapPost:()=> any;
26
+ mapPut: () => any;
27
+ mapPatch: () => any;
28
+ mapDelete: () => any;
29
+ mapView: () => any;
30
+
31
+ // run
32
+ run:(port:number) => void
5
33
 
34
+ }
6
35
 
7
36
 
8
37
  export interface InlineRoutes{
package/src/cache.ts ADDED
@@ -0,0 +1,91 @@
1
+ import type { Redis } from 'ioredis';
2
+
3
+ type CacheEntry<T = any> = {
4
+ data: T;
5
+ timestamp: number;
6
+ };
7
+
8
+ export class CacheManager {
9
+ private store = new Map<string, CacheEntry>();
10
+ private tagsMap = new Map<string, Set<string>>();
11
+ private redis: Redis | null = null;
12
+
13
+ constructor(redisInstance?: Redis) {
14
+ this.redis = redisInstance || null;
15
+ }
16
+
17
+ private redisTagKey(tag: string) {
18
+ return `cache-tags:${tag}`;
19
+ }
20
+
21
+ async get<T>(key: string): Promise<T | null> {
22
+ if (this.redis) {
23
+ const val = await this.redis.get(key);
24
+ return val ? JSON.parse(val) : null;
25
+ }
26
+
27
+ const cached = this.store.get(key);
28
+ return cached ? cached.data : null;
29
+ }
30
+
31
+ async set<T>(
32
+ key: string,
33
+ value: T,
34
+ tags: string[] = [],
35
+ ttl: number = 3600
36
+ ): Promise<void> {
37
+ const entry: CacheEntry<T> = {
38
+ data: value,
39
+ timestamp: Date.now(),
40
+ };
41
+
42
+ if (this.redis) {
43
+ await this.redis.set(key, JSON.stringify(entry.data), 'EX', ttl);
44
+ for (const tag of tags) {
45
+ await this.redis.sadd(this.redisTagKey(tag), key);
46
+ }
47
+ } else {
48
+ this.store.set(key, entry);
49
+ for (const tag of tags) {
50
+ if (!this.tagsMap.has(tag)) this.tagsMap.set(tag, new Set());
51
+ this.tagsMap.get(tag)!.add(key);
52
+ }
53
+ }
54
+ }
55
+
56
+ async delete(key: string): Promise<void> {
57
+ if (this.redis) {
58
+ await this.redis.del(key);
59
+
60
+ // Also clean up from any tag sets
61
+ const tagKeys = await this.redis.keys('cache-tags:*');
62
+ for (const tagKey of tagKeys) {
63
+ await this.redis.srem(tagKey, key);
64
+ }
65
+ } else {
66
+ this.store.delete(key);
67
+ for (const keys of this.tagsMap.values()) {
68
+ keys.delete(key);
69
+ }
70
+ }
71
+ }
72
+
73
+ async invalidateTag(tag: string): Promise<void> {
74
+ if (this.redis) {
75
+ const tagKey = this.redisTagKey(tag);
76
+ const keys = await this.redis.smembers(tagKey);
77
+ if (keys.length) {
78
+ await this.redis.del(...keys); // delete all cached keys
79
+ await this.redis.del(tagKey); // delete the tag set
80
+ }
81
+ } else {
82
+ const keys = this.tagsMap.get(tag);
83
+ if (keys) {
84
+ for (const key of keys) {
85
+ this.store.delete(key);
86
+ }
87
+ this.tagsMap.delete(tag);
88
+ }
89
+ }
90
+ }
91
+ }
package/src/config.ts CHANGED
@@ -7,6 +7,9 @@
7
7
  import { Container, Service, Constructable } from "typedi";
8
8
  import { Environment } from "./environment-variables";
9
9
 
10
+
11
+
12
+
10
13
  export interface IConfig<T = any> {
11
14
  config(env: Environment): T;
12
15
  }
package/src/controller.ts CHANGED
@@ -5,7 +5,6 @@
5
5
  * @url https://github.com/xtareq
6
6
  */
7
7
 
8
-
9
8
  import Container, { Service } from "typedi";
10
9
  import container, {
11
10
  API_CONTROLLER_METADATA_KEY,
@@ -100,7 +99,7 @@ export function ApiController(
100
99
  options?: ControllerOptions,
101
100
  ): ClassDecorator;
102
101
  export function ApiController(
103
- pathOrOptions: Function |string | ControllerOptions = "/",
102
+ pathOrOptions: Function | string | ControllerOptions = "/",
104
103
  mayBeOptions?: ControllerOptions,
105
104
  ): any {
106
105
  if (typeof pathOrOptions == 'function') {
@@ -11,7 +11,7 @@ export abstract class BaseHttpException extends Error {
11
11
  super(JSON.stringify(message));
12
12
  }
13
13
  isCustomException() {
14
- return true;
14
+ return true;
15
15
  }
16
16
  }
17
17
 
package/src/icore.ts CHANGED
@@ -159,23 +159,29 @@ type AvleonApp = FastifyInstance;
159
159
 
160
160
  export type TestAppOptions = {
161
161
  controllers: Constructor[];
162
+ dataSource?: DataSource
162
163
  };
163
164
 
164
165
  export interface AvleonTestAppliction {
165
166
  addDataSource: (dataSourceOptions: DataSourceOptions) => void;
166
167
  getApp: (options?: TestAppOptions) => any;
167
- getController: <T>(controller: Constructor<T>) => T;
168
+ getController: <T>(controller: Constructor<T>, deps: any[]) => T;
169
+ }
170
+
171
+ export type AutoControllerOptions = {
172
+ auto: true,
173
+ path?: string
168
174
  }
169
175
  export interface IAvleonApplication {
170
176
  isDevelopment(): boolean;
171
177
  useCors(corsOptions?: FastifyCorsOptions): void;
172
178
  useDataSource<
173
- T extends IConfig<R>,
174
- R = ReturnType<InstanceType<Constructable<T>>["config"]>,
175
- >(
176
- ConfigClass: Constructable<T>,
177
- modifyConfig?: (config: R) => R,
178
- ): void;
179
+ T extends IConfig<R>,
180
+ R = ReturnType<InstanceType<Constructable<T>>["config"]>,
181
+ >(
182
+ ConfigClass: Constructable<T>,
183
+ modifyConfig?: (config: R) => R,
184
+ ): void;
179
185
  useOpenApi<
180
186
  T extends IConfig<R>,
181
187
  R = ReturnType<InstanceType<Constructable<T>>["config"]>,
@@ -183,8 +189,9 @@ export interface IAvleonApplication {
183
189
  ConfigClass: Constructable<T>,
184
190
  modifyConfig?: (config: R) => R,
185
191
  ): void;
186
- useSwagger(options: OpenApiUiOptions): Promise<void>; // Deprecated
192
+
187
193
  useMultipart(options: MultipartOptions): void;
194
+ useCache(options: any): void
188
195
 
189
196
  useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]): void;
190
197
  useAuthoriztion<T extends any>(middleware: Constructor<T>): void;
@@ -198,12 +205,15 @@ export interface IAvleonApplication {
198
205
  mapPost<T extends (...args: any[]) => any>(path: string, fn: T): any;
199
206
  mapPut<T extends (...args: any[]) => any>(path: string, fn: T): any;
200
207
  mapDelete<T extends (...args: any[]) => any>(path: string, fn: T): any;
201
- mapControllers(controllers: any[]): any;
208
+ useControllers(controllers: any[]): any;
209
+ useControllers(controllersOptions: AutoControllerOptions): any;
210
+ useControllers(controllersOrOptions: any[] | AutoControllerOptions): any;
202
211
  useStaticFiles(options?: StaticFileOptions): void;
203
212
  run(port?: number): Promise<void>;
204
213
  getTestApp(): TestApplication;
205
214
  }
206
-
215
+ type OpenApiConfigClass<T = any> = Constructable<IConfig<T>>;
216
+ type OpenApiConfigInput<T = any> = OpenApiConfigClass<T> | T;
207
217
  // InternalApplication
208
218
  export class AvleonApplication {
209
219
  private static instance: AvleonApplication;
@@ -222,6 +232,8 @@ export class AvleonApplication {
222
232
  private appConfig: AppConfig;
223
233
  private dataSource?: DataSource = undefined;
224
234
  private isMapFeatures = false;
235
+ private registerControllerAuto = false;
236
+ private registerControllerPath = './src';
225
237
 
226
238
  private metaCache = new Map<string, MethodParamMeta>();
227
239
  private multipartOptions: FastifyMultipartOptions | undefined;
@@ -314,31 +326,48 @@ export class AvleonApplication {
314
326
  useCors(corsOptions: FastifyCorsOptions = {}) {
315
327
  this.app.register(cors, corsOptions);
316
328
  }
317
- useOpenApi<
318
- T extends IConfig<R>,
319
- R = ReturnType<InstanceType<Constructable<T>>["config"]>,
320
- >(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R) {
321
- const openApiConfig: R = this.appConfig.get(ConfigClass);
329
+
330
+ useOpenApi<T = OpenApiUiOptions>(
331
+ configOrClass: OpenApiConfigInput<T>,
332
+ modifyConfig?: (config: T) => T
333
+ ) {
334
+ let openApiConfig: T;
335
+
336
+ const isClass = (input: any): input is OpenApiConfigClass<T> => {
337
+ return (
338
+ typeof input === 'function' &&
339
+ typeof input.prototype === 'object' &&
340
+ input.prototype?.constructor === input
341
+ );
342
+ };
343
+
344
+ if (isClass(configOrClass)) {
345
+ // It's a class constructor
346
+ openApiConfig = this.appConfig.get(configOrClass);
347
+ } else {
348
+ // It's a plain object
349
+ openApiConfig = configOrClass as T;
350
+ }
351
+
322
352
  if (modifyConfig) {
323
- const modifiedConfig: R = modifyConfig(openApiConfig);
324
- this.globalSwaggerOptions = modifiedConfig;
353
+ this.globalSwaggerOptions = modifyConfig(openApiConfig);
325
354
  } else {
326
355
  this.globalSwaggerOptions = openApiConfig;
327
356
  }
357
+
328
358
  this.hasSwagger = true;
329
359
  }
330
360
 
331
361
 
332
-
333
- /**
334
- * Registers the fastify-multipart plugin with the Fastify instance.
335
- * This enables handling of multipart/form-data requests, typically used for file uploads.
336
- *
337
- * @param {MultipartOptions} options - Options to configure the fastify-multipart plugin.
338
- * @param {FastifyInstance} this.app - The Fastify instance to register the plugin with.
339
- * @property {MultipartOptions} this.multipartOptions - Stores the provided multipart options.
340
- * @see {@link https://github.com/fastify/fastify-multipart} for more details on available options.
341
- */
362
+ /**
363
+ * Registers the fastify-multipart plugin with the Fastify instance.
364
+ * This enables handling of multipart/form-data requests, typically used for file uploads.
365
+ *
366
+ * @param {MultipartOptions} options - Options to configure the fastify-multipart plugin.
367
+ * @param {FastifyInstance} this.app - The Fastify instance to register the plugin with.
368
+ * @property {MultipartOptions} this.multipartOptions - Stores the provided multipart options.
369
+ * @see {@link https://github.com/fastify/fastify-multipart} for more details on available options.
370
+ */
342
371
  useMultipart(options: MultipartOptions) {
343
372
  this.multipartOptions = options;
344
373
  this.app.register(fastifyMultipart, {
@@ -348,57 +377,60 @@ export class AvleonApplication {
348
377
  }
349
378
 
350
379
 
351
- /**
352
- * Configures and initializes a TypeORM DataSource based on the provided configuration class.
353
- * It retrieves the configuration from the application's configuration service and allows for optional modification.
354
- * The initialized DataSource is then stored and registered within a dependency injection container.
355
- *
356
- * @template T - A generic type extending the `IConfig` interface, representing the configuration class.
357
- * @template R - A generic type representing the return type of the configuration method of the `ConfigClass`.
358
- * @param {Constructable<T>} ConfigClass - The constructor of the configuration class to be used for creating the DataSource.
359
- * @param {(config: R) => R} [modifyConfig] - An optional function that takes the initial configuration and returns a modified configuration.
360
- * @returns {void}
361
- * @property {DataSourceOptions} this.dataSourceOptions - Stores the final DataSource options after potential modification.
362
- * @property {DataSource} this.dataSource - Stores the initialized TypeORM DataSource instance.
363
- * @see {@link https://typeorm.io/} for more information about TypeORM.
364
- */
380
+ /**
381
+ * Configures and initializes a TypeORM DataSource based on the provided configuration class.
382
+ * It retrieves the configuration from the application's configuration service and allows for optional modification.
383
+ * The initialized DataSource is then stored and registered within a dependency injection container.
384
+ *
385
+ * @template T - A generic type extending the `IConfig` interface, representing the configuration class.
386
+ * @template R - A generic type representing the return type of the configuration method of the `ConfigClass`.
387
+ * @param {Constructable<T>} ConfigClass - The constructor of the configuration class to be used for creating the DataSource.
388
+ * @param {(config: R) => R} [modifyConfig] - An optional function that takes the initial configuration and returns a modified configuration.
389
+ * @returns {void}
390
+ * @property {DataSourceOptions} this.dataSourceOptions - Stores the final DataSource options after potential modification.
391
+ * @property {DataSource} this.dataSource - Stores the initialized TypeORM DataSource instance.
392
+ * @see {@link https://typeorm.io/} for more information about TypeORM.
393
+ */
365
394
  useDataSource<
366
- T extends IConfig<R>,
367
- R = ReturnType<InstanceType<Constructable<T>>["config"]>,
368
- >(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R) {
369
- const dsConfig: R = this.appConfig.get(ConfigClass);
370
- if (modifyConfig) {
371
- const modifiedConfig: R = modifyConfig(dsConfig);
372
- this.dataSourceOptions = modifiedConfig as unknown as DataSourceOptions;
373
- } else {
374
- this.dataSourceOptions = dsConfig as unknown as DataSourceOptions;
375
- }
395
+ T extends IConfig<R>,
396
+ R = ReturnType<InstanceType<Constructable<T>>["config"]>,
397
+ >(ConfigClass: Constructable<T>, modifyConfig?: (config: R) => R) {
398
+ const dsConfig: R = this.appConfig.get(ConfigClass);
399
+ if (modifyConfig) {
400
+ const modifiedConfig: R = modifyConfig(dsConfig);
401
+ this.dataSourceOptions = modifiedConfig as unknown as DataSourceOptions;
402
+ } else {
403
+ this.dataSourceOptions = dsConfig as unknown as DataSourceOptions;
404
+ }
376
405
 
377
- const typeorm = require("typeorm");
378
- const datasource = new typeorm.DataSource(
379
- dsConfig,
380
- ) as DataSource;
406
+ const typeorm = require("typeorm");
407
+ const datasource = new typeorm.DataSource(
408
+ dsConfig,
409
+ ) as DataSource;
381
410
 
382
- this.dataSource = datasource;
411
+ this.dataSource = datasource;
383
412
 
384
- Container.set<DataSource>("idatasource",
385
- datasource,
386
- );
387
- }
413
+ Container.set<DataSource>("idatasource",
414
+ datasource,
415
+ );
416
+ }
388
417
 
418
+ useCache(options: any) {
389
419
 
390
- /**
391
- * Registers an array of middleware classes to be executed before request handlers.
392
- * It retrieves instances of the middleware classes from the dependency injection container
393
- * and adds them as 'preHandler' hooks to the Fastify application.
394
- *
395
- * @template T - A generic type extending the `AppMiddleware` interface, representing the middleware class.
396
- * @param {Constructor<T>[]} mclasses - An array of middleware class constructors to be registered.
397
- * @returns {void}
398
- * @property {Map<string, T>} this.middlewares - Stores the registered middleware instances, keyed by their class names.
399
- * @see {@link https://www.fastify.io/docs/latest/Reference/Hooks/#prehandler} for more information about Fastify preHandler hooks.
400
- */
401
- useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
420
+ }
421
+
422
+ /**
423
+ * Registers an array of middleware classes to be executed before request handlers.
424
+ * It retrieves instances of the middleware classes from the dependency injection container
425
+ * and adds them as 'preHandler' hooks to the Fastify application.
426
+ *
427
+ * @template T - A generic type extending the `AppMiddleware` interface, representing the middleware class.
428
+ * @param {Constructor<T>[]} mclasses - An array of middleware class constructors to be registered.
429
+ * @returns {void}
430
+ * @property {Map<string, T>} this.middlewares - Stores the registered middleware instances, keyed by their class names.
431
+ * @see {@link https://www.fastify.io/docs/latest/Reference/Hooks/#prehandler} for more information about Fastify preHandler hooks.
432
+ */
433
+ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
402
434
  for (const mclass of mclasses) {
403
435
  const cls = Container.get<T>(mclass);
404
436
  this.middlewares.set(mclass.name, cls);
@@ -407,31 +439,31 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
407
439
  }
408
440
 
409
441
 
410
- /**
411
- * Registers a middleware constructor to be used for authorization purposes.
412
- * The specific implementation and usage of this middleware will depend on the application's authorization logic.
413
- *
414
- * @template T - A generic type representing the constructor of the authorization middleware.
415
- * @param {Constructor<T>} middleware - The constructor of the middleware to be used for authorization.
416
- * @returns {void}
417
- * @property {any} this.authorizeMiddleware - Stores the constructor of the authorization middleware.
418
- */
442
+ /**
443
+ * Registers a middleware constructor to be used for authorization purposes.
444
+ * The specific implementation and usage of this middleware will depend on the application's authorization logic.
445
+ *
446
+ * @template T - A generic type representing the constructor of the authorization middleware.
447
+ * @param {Constructor<T>} middleware - The constructor of the middleware to be used for authorization.
448
+ * @returns {void}
449
+ * @property {any} this.authorizeMiddleware - Stores the constructor of the authorization middleware.
450
+ */
419
451
  useAuthoriztion<T extends any>(middleware: Constructor<T>) {
420
452
  this.authorizeMiddleware = middleware as any;
421
453
  }
422
454
 
423
455
 
424
456
 
425
- /**
426
- * Registers the `@fastify/static` plugin to serve static files.
427
- * It configures the root directory and URL prefix for serving static assets.
428
- *
429
- * @param {StaticFileOptions} [options={ path: undefined, prefix: undefined }] - Optional configuration for serving static files.
430
- * @param {string} [options.path] - The absolute path to the static files directory. Defaults to 'process.cwd()/public'.
431
- * @param {string} [options.prefix] - The URL prefix for serving static files. Defaults to '/static/'.
432
- * @returns {void}
433
- * @see {@link https://github.com/fastify/fastify-static} for more details on available options.
434
- */
457
+ /**
458
+ * Registers the `@fastify/static` plugin to serve static files.
459
+ * It configures the root directory and URL prefix for serving static assets.
460
+ *
461
+ * @param {StaticFileOptions} [options={ path: undefined, prefix: undefined }] - Optional configuration for serving static files.
462
+ * @param {string} [options.path] - The absolute path to the static files directory. Defaults to 'process.cwd()/public'.
463
+ * @param {string} [options.prefix] - The URL prefix for serving static files. Defaults to '/static/'.
464
+ * @returns {void}
465
+ * @see {@link https://github.com/fastify/fastify-static} for more details on available options.
466
+ */
435
467
  useStaticFiles(
436
468
  options: StaticFileOptions = { path: undefined, prefix: undefined },
437
469
  ) {
@@ -535,7 +567,7 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
535
567
  handler: async (req, res) => {
536
568
  let reqClone = req as IRequest;
537
569
 
538
-
570
+
539
571
  // class level authrization
540
572
  if (authClsMeata.authorize && this.authorizeMiddleware) {
541
573
  const cls = container.get(this.authorizeMiddleware) as any;
@@ -714,37 +746,74 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
714
746
  return meta;
715
747
  }
716
748
 
717
- async autoControllers() {
718
- const controllers: Function[] = [];
719
- const files = await fs.readdir(controllerDir);
749
+ private _resolveControllerDir(dir?: string) {
750
+ const isTsNode =
751
+ process.env.TS_NODE_DEV ||
752
+ process.env.TS_NODE_PROJECT ||
753
+ (process as any)[Symbol.for("ts-node.register.instance")];
754
+ const controllerDir = path.join(
755
+ process.cwd(),
756
+ this.registerControllerPath
757
+ );
758
+
759
+ return isTsNode ? controllerDir : controllerDir.replace('src', 'dist')
760
+ }
761
+
762
+ private async autoControllers(controllersPath?: string) {
763
+ const conDir = this._resolveControllerDir(controllersPath);
764
+
765
+ const files = await fs.readdir(conDir, { recursive: true });
720
766
  for (const file of files) {
767
+ const isTestFile = /\.(test|spec|e2e-spec)\.ts$/.test(file);
768
+ if (isTestFile) continue;
721
769
  if (isTsNode ? file.endsWith(".ts") : file.endsWith(".js")) {
722
- const filePath = path.join(controllerDir, file);
770
+ const filePath = path.join(conDir, file);
723
771
  const module = await import(filePath);
724
772
  for (const exported of Object.values(module)) {
725
773
  if (typeof exported === "function" && isApiController(exported)) {
726
- this.buildController(exported);
774
+ console.log('adding', exported.name)
775
+ if (!this.controllers.some(con => exported.name == con.name)) {
776
+ this.controllers.push(exported)
777
+ }
778
+
779
+ //this.buildController(exported);
727
780
  }
728
781
  }
729
782
  }
730
783
  }
731
784
  }
732
785
 
733
- mapControllers(controllers: Function[]) {
734
- this.controllers = controllers;
786
+ useControllers(controllers: Constructor[] | AutoControllerOptions) {
787
+
788
+ if (Array.isArray(controllers)) {
789
+ this.controllers = controllers;
790
+
791
+ controllers.forEach(controller => {
792
+ if (!this.controllers.includes(controller)) {
793
+ this.controllers.push(controller)
794
+ }
795
+ })
796
+ } else {
797
+ this.registerControllerAuto = true;
798
+ if (controllers.path) {
799
+ this.registerControllerPath = controllers.path;
800
+ }
801
+ }
802
+
735
803
  }
736
804
 
737
805
  // addFeature(feature:{controllers:Function[]}){
738
806
  // feature.controllers.forEach(c=> this.controllers.push(c))
739
807
  // }
740
808
 
741
- mapFeature(){
742
- if(!this.isMapFeatures){
743
- this.isMapFeatures = true;
744
- }
745
- }
809
+ // mapFeature(){
810
+ // if(!this.isMapFeatures){
811
+ // this.isMapFeatures = true;
812
+ // }
813
+ // }
746
814
 
747
815
  private async _mapControllers() {
816
+
748
817
  if (this.controllers.length > 0) {
749
818
  for (let controller of this.controllers) {
750
819
  if (isApiController(controller)) {
@@ -756,14 +825,11 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
756
825
  }
757
826
  }
758
827
 
759
- mapControllersAuto() {
760
- const isExists = existsSync(controllerDir);
761
- if (isExists) {
762
- this.autoControllers();
763
- }
764
- }
828
+ // useControllersAuto(controllerPath?:string) {
829
+ // this.registerControllerAuto = true;
830
+ // //this.autoControllers();
831
+ // }
765
832
 
766
- async handleRoute(args: any) { }
767
833
 
768
834
  private async mapFn(fn: Function) {
769
835
  const original = fn;
@@ -827,8 +893,7 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
827
893
  middlewares: [],
828
894
  schema: {},
829
895
  });
830
-
831
- this.mapFn(fn);
896
+ // this.mapFn(fn);
832
897
 
833
898
  const route = {
834
899
  useMiddleware: <M extends AppMiddleware>(
@@ -878,7 +943,7 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
878
943
  }
879
944
 
880
945
 
881
- private _mapFeatures(){
946
+ private _mapFeatures() {
882
947
  const features = Container.get('features');
883
948
  console.log('Features', features);
884
949
  }
@@ -889,7 +954,7 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
889
954
  }
890
955
  }
891
956
 
892
- async run(port: number = 4000, fn?:CallableFunction): Promise<void> {
957
+ async run(port: number = 4000, fn?: CallableFunction): Promise<void> {
893
958
  if (this.alreadyRun) throw new SystemUseError("App already running");
894
959
  this.alreadyRun = true;
895
960
 
@@ -897,9 +962,13 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
897
962
  await this.initSwagger(this.globalSwaggerOptions);
898
963
  }
899
964
  await this.initializeDatabase();
900
- if(this.isMapFeatures){
965
+ if (this.isMapFeatures) {
901
966
  this._mapFeatures();
902
967
  }
968
+
969
+ if (this.registerControllerAuto) {
970
+ await this.autoControllers();
971
+ }
903
972
  await this._mapControllers();
904
973
 
905
974
  this.rMap.forEach((value, key) => {
@@ -975,8 +1044,15 @@ useMiddlewares<T extends AppMiddleware>(mclasses: Constructor<T>[]) {
975
1044
  this.app.inject({ method: "DELETE", url, ...options }),
976
1045
  options: async (url: string, options?: InjectOptions) =>
977
1046
  this.app.inject({ method: "OPTIONS", url, ...options }),
978
- getController: <T>(controller: Constructor<T>) => {
979
- return Container.get<T>(controller);
1047
+ getController: <T>(controller: Constructor<T>, deps: any[] = []) => {
1048
+ const paramTypes = Reflect.getMetadata('design:paramtypes', controller) || [];
1049
+
1050
+ deps.forEach((dep, i) => {
1051
+ Container.set(paramTypes[i], dep);
1052
+ });
1053
+
1054
+ return Container.get(controller);
1055
+
980
1056
  },
981
1057
  };
982
1058
  } catch (error) {
@@ -1009,26 +1085,25 @@ export interface IAppBuilder {
1009
1085
  build<T extends IAvleonApplication>(): T;
1010
1086
  }
1011
1087
 
1012
- export class TestBuilder {
1013
- private static instance: TestBuilder;
1088
+ export class AvleonTest {
1089
+ private static instance: AvleonTest;
1014
1090
  private app: any;
1015
1091
  private dataSourceOptions?: DataSourceOptions | undefined;
1016
1092
  private constructor() {
1017
1093
  process.env.NODE_ENV = "test";
1018
1094
  }
1019
1095
 
1020
- static createBuilder() {
1021
- if (!TestBuilder.instance) {
1022
- TestBuilder.instance = new TestBuilder();
1023
- }
1024
- return TestBuilder.instance;
1025
- }
1026
-
1027
- addDatasource(options: DataSourceOptions) {
1096
+ private addDatasource(options: DataSourceOptions) {
1028
1097
  this.dataSourceOptions = options;
1029
1098
  }
1030
1099
 
1031
- private getController<T>(controller: Constructor<T>) {
1100
+ getController<T>(controller: Constructor<T>, deps: any[] = []) {
1101
+ const paramTypes = Reflect.getMetadata('design:paramtypes', controller) || [];
1102
+
1103
+ deps.forEach((dep, i) => {
1104
+ Container.set(paramTypes[i], dep);
1105
+ });
1106
+
1032
1107
  return Container.get(controller);
1033
1108
  }
1034
1109
 
@@ -1036,27 +1111,28 @@ export class TestBuilder {
1036
1111
  return Container.get(service);
1037
1112
  }
1038
1113
 
1039
- getTestApplication(options: TestAppOptions) {
1114
+
1115
+ static createTestApplication(options: TestAppOptions) {
1040
1116
  const app = AvleonApplication.getInternalApp({
1041
- dataSourceOptions: this.dataSourceOptions,
1117
+ dataSourceOptions: options.dataSource ? options.dataSource : null,
1042
1118
  });
1043
- app.mapControllers([...options.controllers]);
1119
+ app.useControllers([...options.controllers]);
1044
1120
  return app.getTestApp();
1045
1121
  }
1046
1122
 
1047
- build(app: IAvleonApplication) {
1123
+ static from(app: AvleonApplication) {
1048
1124
  return app.getTestApp();
1049
1125
  }
1050
1126
 
1051
- fromApplication(app: IAvleonApplication) {
1052
- return app.getTestApp();
1127
+ static clean() {
1128
+ Container.reset();
1053
1129
  }
1054
1130
  }
1055
1131
 
1056
- export class Builder {
1132
+ export class Avleon {
1057
1133
 
1058
- static createApplication(){
1059
- const app = AvleonApplication.getApp();
1134
+ static createApplication() {
1135
+ const app = AvleonApplication.getApp();
1060
1136
  return app
1061
1137
  }
1062
1138
 
package/src/testing.ts CHANGED
@@ -134,7 +134,7 @@ export class AvleonTestBuilder {
134
134
  });
135
135
 
136
136
  // Map controllers
137
- app.mapControllers(this.controllers);
137
+ app.useControllers(this.controllers);
138
138
 
139
139
  // Get test application
140
140
  return app.getTestApp();