@impactor/nest 3.0.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 (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -0
  3. package/index.d.ts +40 -0
  4. package/nx.json +190 -0
  5. package/package.json +102 -0
  6. package/src/configs/app.d.ts +21 -0
  7. package/src/configs/app.js +65 -0
  8. package/src/configs/app.js.map +1 -0
  9. package/src/configs/database.d.ts +2 -0
  10. package/src/configs/database.js +23 -0
  11. package/src/configs/database.js.map +1 -0
  12. package/src/configs/microservice.d.ts +7 -0
  13. package/src/configs/microservice.js +26 -0
  14. package/src/configs/microservice.js.map +1 -0
  15. package/src/configs/multi-queue-rabbitmq-server.d.ts +24 -0
  16. package/src/configs/multi-queue-rabbitmq-server.js +91 -0
  17. package/src/configs/multi-queue-rabbitmq-server.js.map +1 -0
  18. package/src/configs/redis-config.d.ts +5 -0
  19. package/src/configs/redis-config.js +16 -0
  20. package/src/configs/redis-config.js.map +1 -0
  21. package/src/create-app.d.ts +3 -0
  22. package/src/create-app.js +104 -0
  23. package/src/create-app.js.map +1 -0
  24. package/src/create-microservice.d.ts +2 -0
  25. package/src/create-microservice.js +43 -0
  26. package/src/create-microservice.js.map +1 -0
  27. package/src/create-testing-microservice.d.ts +3 -0
  28. package/src/create-testing-microservice.js +30 -0
  29. package/src/create-testing-microservice.js.map +1 -0
  30. package/src/decorators/controller/add-decorators/add-controller-path.d.ts +2 -0
  31. package/src/decorators/controller/add-decorators/add-controller-path.js +22 -0
  32. package/src/decorators/controller/add-decorators/add-controller-path.js.map +1 -0
  33. package/src/decorators/controller/add-decorators/add-controller-tags.d.ts +2 -0
  34. package/src/decorators/controller/add-decorators/add-controller-tags.js +28 -0
  35. package/src/decorators/controller/add-decorators/add-controller-tags.js.map +1 -0
  36. package/src/decorators/controller/add-decorators/add-http-method.d.ts +3 -0
  37. package/src/decorators/controller/add-decorators/add-http-method.js +27 -0
  38. package/src/decorators/controller/add-decorators/add-http-method.js.map +1 -0
  39. package/src/decorators/controller/add-decorators/add-swagger-body.d.ts +3 -0
  40. package/src/decorators/controller/add-decorators/add-swagger-body.js +30 -0
  41. package/src/decorators/controller/add-decorators/add-swagger-body.js.map +1 -0
  42. package/src/decorators/controller/add-decorators/add-swagger-operation.d.ts +3 -0
  43. package/src/decorators/controller/add-decorators/add-swagger-operation.js +21 -0
  44. package/src/decorators/controller/add-decorators/add-swagger-operation.js.map +1 -0
  45. package/src/decorators/controller/add-decorators/add-swagger-params.d.ts +3 -0
  46. package/src/decorators/controller/add-decorators/add-swagger-params.js +50 -0
  47. package/src/decorators/controller/add-decorators/add-swagger-params.js.map +1 -0
  48. package/src/decorators/controller/add-decorators/add-swagger-response.d.ts +3 -0
  49. package/src/decorators/controller/add-decorators/add-swagger-response.js +33 -0
  50. package/src/decorators/controller/add-decorators/add-swagger-response.js.map +1 -0
  51. package/src/decorators/controller/controller.decorator.d.ts +2 -0
  52. package/src/decorators/controller/controller.decorator.js +18 -0
  53. package/src/decorators/controller/controller.decorator.js.map +1 -0
  54. package/src/decorators/controller/controller.factory.d.ts +11 -0
  55. package/src/decorators/controller/controller.factory.js +261 -0
  56. package/src/decorators/controller/controller.factory.js.map +1 -0
  57. package/src/decorators/controller/default-options.d.ts +2 -0
  58. package/src/decorators/controller/default-options.js +181 -0
  59. package/src/decorators/controller/default-options.js.map +1 -0
  60. package/src/decorators/controller/dto/empty.dto.d.ts +2 -0
  61. package/src/decorators/controller/dto/empty.dto.js +18 -0
  62. package/src/decorators/controller/dto/empty.dto.js.map +1 -0
  63. package/src/decorators/controller/dto/update-response.dto.d.ts +3 -0
  64. package/src/decorators/controller/dto/update-response.dto.js +16 -0
  65. package/src/decorators/controller/dto/update-response.dto.js.map +1 -0
  66. package/src/decorators/controller/interceptors/query.interceptor.d.ts +8 -0
  67. package/src/decorators/controller/interceptors/query.interceptor.js +59 -0
  68. package/src/decorators/controller/interceptors/query.interceptor.js.map +1 -0
  69. package/src/decorators/controller/route-handler.d.ts +3 -0
  70. package/src/decorators/controller/route-handler.js +44 -0
  71. package/src/decorators/controller/route-handler.js.map +1 -0
  72. package/src/decorators/controller/route.decorator.d.ts +2 -0
  73. package/src/decorators/controller/route.decorator.js +17 -0
  74. package/src/decorators/controller/route.decorator.js.map +1 -0
  75. package/src/decorators/controller/services/crud-typeorm.service.d.ts +46 -0
  76. package/src/decorators/controller/services/crud-typeorm.service.js +232 -0
  77. package/src/decorators/controller/services/crud-typeorm.service.js.map +1 -0
  78. package/src/decorators/controller/types/index.d.ts +54 -0
  79. package/src/decorators/controller/types/index.js +6 -0
  80. package/src/decorators/controller/types/index.js.map +1 -0
  81. package/src/decorators/controller/utils/merge-options.d.ts +2 -0
  82. package/src/decorators/controller/utils/merge-options.js +37 -0
  83. package/src/decorators/controller/utils/merge-options.js.map +1 -0
  84. package/src/decorators/controller/utils/reflect.d.ts +10 -0
  85. package/src/decorators/controller/utils/reflect.js +86 -0
  86. package/src/decorators/controller/utils/reflect.js.map +1 -0
  87. package/src/decorators/prop.decorator.d.ts +9 -0
  88. package/src/decorators/prop.decorator.js +80 -0
  89. package/src/decorators/prop.decorator.js.map +1 -0
  90. package/src/exceptions/rpc-bad-request.exception.d.ts +4 -0
  91. package/src/exceptions/rpc-bad-request.exception.js +19 -0
  92. package/src/exceptions/rpc-bad-request.exception.js.map +1 -0
  93. package/src/exceptions/rpc-base.exception.d.ts +6 -0
  94. package/src/exceptions/rpc-base.exception.js +22 -0
  95. package/src/exceptions/rpc-base.exception.js.map +1 -0
  96. package/src/exceptions/rpc-conflict.exception.d.ts +4 -0
  97. package/src/exceptions/rpc-conflict.exception.js +19 -0
  98. package/src/exceptions/rpc-conflict.exception.js.map +1 -0
  99. package/src/exceptions/rpc-internal-server-error.exception.d.ts +4 -0
  100. package/src/exceptions/rpc-internal-server-error.exception.js +19 -0
  101. package/src/exceptions/rpc-internal-server-error.exception.js.map +1 -0
  102. package/src/exceptions/rpc-method-not-allowed.exception.d.ts +4 -0
  103. package/src/exceptions/rpc-method-not-allowed.exception.js +19 -0
  104. package/src/exceptions/rpc-method-not-allowed.exception.js.map +1 -0
  105. package/src/exceptions/rpc-not-found.exception.d.ts +4 -0
  106. package/src/exceptions/rpc-not-found.exception.js +19 -0
  107. package/src/exceptions/rpc-not-found.exception.js.map +1 -0
  108. package/src/exceptions/rpc-not-implemented.exception.d.ts +4 -0
  109. package/src/exceptions/rpc-not-implemented.exception.js +19 -0
  110. package/src/exceptions/rpc-not-implemented.exception.js.map +1 -0
  111. package/src/exceptions/rpc-unauthorized.exception.d.ts +4 -0
  112. package/src/exceptions/rpc-unauthorized.exception.js +19 -0
  113. package/src/exceptions/rpc-unauthorized.exception.js.map +1 -0
  114. package/src/filters/error-to-rpc-exception.filter.d.ts +4 -0
  115. package/src/filters/error-to-rpc-exception.filter.js +48 -0
  116. package/src/filters/error-to-rpc-exception.filter.js.map +1 -0
  117. package/src/filters/error-to-ws-exception.filter.d.ts +4 -0
  118. package/src/filters/error-to-ws-exception.filter.js +47 -0
  119. package/src/filters/error-to-ws-exception.filter.js.map +1 -0
  120. package/src/filters/typeorm-exception.filter.d.ts +6 -0
  121. package/src/filters/typeorm-exception.filter.js +44 -0
  122. package/src/filters/typeorm-exception.filter.js.map +1 -0
  123. package/src/filters/ws-exception.filter.d.ts +5 -0
  124. package/src/filters/ws-exception.filter.js +39 -0
  125. package/src/filters/ws-exception.filter.js.map +1 -0
  126. package/src/generate-metadata.d.ts +10 -0
  127. package/src/generate-metadata.js +97 -0
  128. package/src/generate-metadata.js.map +1 -0
  129. package/src/guards/auth.guard.d.ts +14 -0
  130. package/src/guards/auth.guard.js +92 -0
  131. package/src/guards/auth.guard.js.map +1 -0
  132. package/src/modules/basic/basic.controller.d.ts +3 -0
  133. package/src/modules/basic/basic.controller.js +33 -0
  134. package/src/modules/basic/basic.controller.js.map +1 -0
  135. package/src/modules/basic/basic.module.d.ts +2 -0
  136. package/src/modules/basic/basic.module.js +45 -0
  137. package/src/modules/basic/basic.module.js.map +1 -0
  138. package/src/modules/basic/basic.service.d.ts +2 -0
  139. package/src/modules/basic/basic.service.js +24 -0
  140. package/src/modules/basic/basic.service.js.map +1 -0
  141. package/src/nest-swagger-metadata.js +553 -0
  142. package/src/nest-swagger-metadata.js.map +1 -0
  143. package/src/utils/logger.d.ts +2 -0
  144. package/src/utils/logger.js +14 -0
  145. package/src/utils/logger.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/dto/update-response.dto.ts"],"sourcesContent":["/**\n * a Dto for the response of the update or delete query\n */\nexport class UpdateResponseDto {\n /**\n * the total number of the affecter rows\n */\n affected: number;\n}\n"],"names":["UpdateResponseDto"],"mappings":"AAAA;;CAEC;;;;+BACYA;;;eAAAA;;;AAAN,IAAA,AAAMA,oBAAN,MAAMA;AAKb"}
@@ -0,0 +1,8 @@
1
+ import type { ControllerOptions } from '../types';
2
+ import { type CallHandler, type ExecutionContext, type NestInterceptor } from '@nestjs/common';
3
+ import { Observable } from 'rxjs';
4
+ export declare class QueryInterceptor implements NestInterceptor {
5
+ private readonly query;
6
+ constructor(query: ControllerOptions['query']);
7
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
8
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "QueryInterceptor", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return QueryInterceptor;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _mergeanything = require("merge-anything");
13
+ function _ts_decorate(decorators, target, key, desc) {
14
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
15
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
16
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
17
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
18
+ }
19
+ function _ts_metadata(k, v) {
20
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
21
+ }
22
+ let QueryInterceptor = class QueryInterceptor {
23
+ intercept(context, next) {
24
+ let req = context.switchToHttp().getRequest();
25
+ // trying to parse JSON params
26
+ // client-side stringify JSON params using JSON.stringify
27
+ // for example `?where=JSON.stringify({field: "value"})`
28
+ for(let key in req.query){
29
+ if (typeof req.query[key] === 'string' && [
30
+ '[',
31
+ '{'
32
+ ].includes(req.query[key][0])) {
33
+ try {
34
+ req.query[key] = JSON.parse(req.query[key]);
35
+ } catch {
36
+ // keep the value as string as-is
37
+ }
38
+ }
39
+ }
40
+ // req.query is `Empty <[Object: null prototype] {}>` and not merged, but replaces ths.query
41
+ // https://github.com/mesqueeb/merge-anything/issues/48
42
+ req.query = (0, _mergeanything.merge)(this.query || {}, {
43
+ ...req.query
44
+ });
45
+ return next.handle();
46
+ }
47
+ constructor(query){
48
+ this.query = query;
49
+ }
50
+ };
51
+ QueryInterceptor = _ts_decorate([
52
+ (0, _common.Injectable)(),
53
+ _ts_metadata("design:type", Function),
54
+ _ts_metadata("design:paramtypes", [
55
+ Object
56
+ ])
57
+ ], QueryInterceptor);
58
+
59
+ //# sourceMappingURL=query.interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/interceptors/query.interceptor.ts"],"sourcesContent":["import type { ControllerOptions } from '../types';\nimport {\n type CallHandler,\n type ExecutionContext,\n Injectable,\n type NestInterceptor,\n} from '@nestjs/common';\nimport { Observable } from 'rxjs';\n// @ts-ignore\nimport { merge } from 'merge-anything';\n\n/**\n * merge route query with the current request query.\n *\n * this ensures route.query{} is merged to the existing user routes too,\n * not only the routes that are generated by \\@Controller()\n */\n@Injectable()\nexport class QueryInterceptor implements NestInterceptor {\n constructor(private readonly query: ControllerOptions['query']) {}\n\n intercept(context: ExecutionContext, next: CallHandler): Observable<any> {\n let req = context.switchToHttp().getRequest();\n\n // trying to parse JSON params\n // client-side stringify JSON params using JSON.stringify\n // for example `?where=JSON.stringify({field: \"value\"})`\n for (let key in req.query) {\n if (\n typeof req.query[key] === 'string' &&\n ['[', '{'].includes(req.query[key][0])\n ) {\n try {\n req.query[key] = JSON.parse(req.query[key]);\n } catch {\n // keep the value as string as-is\n }\n }\n }\n\n // req.query is `Empty <[Object: null prototype] {}>` and not merged, but replaces ths.query\n // https://github.com/mesqueeb/merge-anything/issues/48\n req.query = merge(this.query || {}, { ...req.query });\n return next.handle();\n }\n}\n"],"names":["QueryInterceptor","intercept","context","next","req","switchToHttp","getRequest","key","query","includes","JSON","parse","merge","handle"],"mappings":";;;;+BAkBaA;;;eAAAA;;;wBAZN;+BAGe;;;;;;;;;;AASf,IAAA,AAAMA,mBAAN,MAAMA;IAGXC,UAAUC,OAAyB,EAAEC,IAAiB,EAAmB;QACvE,IAAIC,MAAMF,QAAQG,YAAY,GAAGC,UAAU;QAE3C,8BAA8B;QAC9B,yDAAyD;QACzD,wDAAwD;QACxD,IAAK,IAAIC,OAAOH,IAAII,KAAK,CAAE;YACzB,IACE,OAAOJ,IAAII,KAAK,CAACD,IAAI,KAAK,YAC1B;gBAAC;gBAAK;aAAI,CAACE,QAAQ,CAACL,IAAII,KAAK,CAACD,IAAI,CAAC,EAAE,GACrC;gBACA,IAAI;oBACFH,IAAII,KAAK,CAACD,IAAI,GAAGG,KAAKC,KAAK,CAACP,IAAII,KAAK,CAACD,IAAI;gBAC5C,EAAE,OAAM;gBACN,iCAAiC;gBACnC;YACF;QACF;QAEA,4FAA4F;QAC5F,uDAAuD;QACvDH,IAAII,KAAK,GAAGI,IAAAA,oBAAK,EAAC,IAAI,CAACJ,KAAK,IAAI,CAAC,GAAG;YAAE,GAAGJ,IAAII,KAAK;QAAC;QACnD,OAAOL,KAAKU,MAAM;IACpB;IAzBA,YAAY,AAAiBL,KAAiC,CAAE;aAAnCA,QAAAA;IAAoC;AA0BnE"}
@@ -0,0 +1,3 @@
1
+ import { ControllerFactory } from './controller.factory';
2
+ import type { IRoute } from './types';
3
+ export declare function routeHandler(route: IRoute, factory: ControllerFactory): (req: Request, query: any, ...params: any) => any;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "routeHandler", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return routeHandler;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ function routeHandler(route, factory) {
13
+ let serviceName = factory.options?.serviceName || 'service';
14
+ // don't convert this to arrow function, so `this` here refers to the Controller class
15
+ // don't use factory.controller.prototype[serviceName] as service will be undefined
16
+ // todo: query type
17
+ return function(req, query, ...params) {
18
+ // @ts-ignore
19
+ let service = this[serviceName];
20
+ if (!service) {
21
+ throw new _common.HttpException(`this.${serviceName} is not defined, please inject the service`, 500);
22
+ }
23
+ if (!service[route.methodName]) {
24
+ throw new _common.HttpException(`the method this.${serviceName}.${route.methodName}() is not defined in the service`, 500);
25
+ } else if (typeof service[route.methodName] !== 'function') {
26
+ throw new _common.HttpException(`this.${serviceName}.${route.methodName} is not a method`, 500);
27
+ }
28
+ try {
29
+ // todo: send the proper params to each operation
30
+ // getOne(id), postOne(body), patchOne(id,body)
31
+ if (factory.options.maxLimit && query.take > factory.options.maxLimit) {
32
+ query.take = factory.options.maxLimit;
33
+ }
34
+ return service[route.methodName](...params, query, req);
35
+ } catch (error) {
36
+ // todo: get class name using Reflect
37
+ // eslint-disable-next-line no-console
38
+ console.error(`error at ${factory.controller.name}.${route.methodName}()`, error);
39
+ throw new _common.HttpException(error?.message || error, 500);
40
+ }
41
+ };
42
+ }
43
+
44
+ //# sourceMappingURL=route-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/decorators/controller/route-handler.ts"],"sourcesContent":["import { ControllerFactory } from './controller.factory';\nimport type { IRoute } from './types';\nimport { HttpException } from '@nestjs/common';\n\n/**\n * returns a route handler that receives the Request object as its first arg\n */\nexport function routeHandler(route: IRoute, factory: ControllerFactory) {\n let serviceName = factory.options?.serviceName || 'service';\n // don't convert this to arrow function, so `this` here refers to the Controller class\n // don't use factory.controller.prototype[serviceName] as service will be undefined\n // todo: query type\n return function (req: Request, query: any, ...params: any) {\n // @ts-ignore\n let service = this[serviceName];\n\n if (!service) {\n throw new HttpException(\n `this.${serviceName} is not defined, please inject the service`,\n 500,\n );\n }\n\n if (!service[route.methodName]) {\n throw new HttpException(\n `the method this.${serviceName}.${route.methodName}() is not defined in the service`,\n 500,\n );\n } else if (typeof service[route.methodName] !== 'function') {\n throw new HttpException(\n `this.${serviceName}.${route.methodName} is not a method`,\n 500,\n );\n }\n\n try {\n // todo: send the proper params to each operation\n // getOne(id), postOne(body), patchOne(id,body)\n if (factory.options.maxLimit && query.take > factory.options.maxLimit) {\n query.take = factory.options.maxLimit;\n }\n return service[route.methodName](...params, query, req);\n } catch (error: any) {\n // todo: get class name using Reflect\n // eslint-disable-next-line no-console\n console.error(\n `error at ${factory.controller.name}.${route.methodName}()`,\n error,\n );\n throw new HttpException(error?.message || error, 500);\n }\n };\n}\n"],"names":["routeHandler","route","factory","serviceName","options","req","query","params","service","HttpException","methodName","maxLimit","take","error","console","controller","name","message"],"mappings":";;;;+BAOgBA;;;eAAAA;;;wBALc;AAKvB,SAASA,aAAaC,KAAa,EAAEC,OAA0B;IACpE,IAAIC,cAAcD,QAAQE,OAAO,EAAED,eAAe;IAClD,sFAAsF;IACtF,mFAAmF;IACnF,mBAAmB;IACnB,OAAO,SAAUE,GAAY,EAAEC,KAAU,EAAE,GAAGC,MAAW;QACvD,aAAa;QACb,IAAIC,UAAU,IAAI,CAACL,YAAY;QAE/B,IAAI,CAACK,SAAS;YACZ,MAAM,IAAIC,qBAAa,CACrB,CAAC,KAAK,EAAEN,YAAY,0CAA0C,CAAC,EAC/D;QAEJ;QAEA,IAAI,CAACK,OAAO,CAACP,MAAMS,UAAU,CAAC,EAAE;YAC9B,MAAM,IAAID,qBAAa,CACrB,CAAC,gBAAgB,EAAEN,YAAY,CAAC,EAAEF,MAAMS,UAAU,CAAC,gCAAgC,CAAC,EACpF;QAEJ,OAAO,IAAI,OAAOF,OAAO,CAACP,MAAMS,UAAU,CAAC,KAAK,YAAY;YAC1D,MAAM,IAAID,qBAAa,CACrB,CAAC,KAAK,EAAEN,YAAY,CAAC,EAAEF,MAAMS,UAAU,CAAC,gBAAgB,CAAC,EACzD;QAEJ;QAEA,IAAI;YACF,iDAAiD;YACjD,+CAA+C;YAC/C,IAAIR,QAAQE,OAAO,CAACO,QAAQ,IAAIL,MAAMM,IAAI,GAAGV,QAAQE,OAAO,CAACO,QAAQ,EAAE;gBACrEL,MAAMM,IAAI,GAAGV,QAAQE,OAAO,CAACO,QAAQ;YACvC;YACA,OAAOH,OAAO,CAACP,MAAMS,UAAU,CAAC,IAAIH,QAAQD,OAAOD;QACrD,EAAE,OAAOQ,OAAY;YACnB,qCAAqC;YACrC,sCAAsC;YACtCC,QAAQD,KAAK,CACX,CAAC,SAAS,EAAEX,QAAQa,UAAU,CAACC,IAAI,CAAC,CAAC,EAAEf,MAAMS,UAAU,CAAC,EAAE,CAAC,EAC3DG;YAEF,MAAM,IAAIJ,qBAAa,CAACI,OAAOI,WAAWJ,OAAO;QACnD;IACF;AACF"}
@@ -0,0 +1,2 @@
1
+ import { InitRoute } from './types';
2
+ export declare function Route(route?: InitRoute): MethodDecorator;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "Route", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return Route;
9
+ }
10
+ });
11
+ function Route(route) {
12
+ return (target, property)=>{
13
+ Reflect.defineMetadata('routeOptions', route, target, property);
14
+ };
15
+ }
16
+
17
+ //# sourceMappingURL=route.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/decorators/controller/route.decorator.ts"],"sourcesContent":["import { InitRoute } from './types';\n\n/**\n * add route options to the method to override the options set by `@Controller()`\n *\n * @example\n * ```\n * \\@Route({httpMethod: 'GET', many: true})\n * getAllUsers(){\n * return this.service.getAllUsers();\n * }\n * ```\n */\nexport function Route(route?: InitRoute): MethodDecorator {\n return (target: Object, property: string | symbol) => {\n Reflect.defineMetadata('routeOptions', route, target, property);\n };\n}\n"],"names":["Route","route","target","property","Reflect","defineMetadata"],"mappings":";;;;+BAagBA;;;eAAAA;;;AAAT,SAASA,MAAMC,KAAiB;IACrC,OAAO,CAACC,QAAgBC;QACtBC,QAAQC,cAAc,CAAC,gBAAgBJ,OAAOC,QAAQC;IACxD;AACF"}
@@ -0,0 +1,46 @@
1
+ import { type FindManyOptions, type ObjectLiteral, Repository } from 'typeorm';
2
+ import type { DeepPartial } from 'typeorm/common/DeepPartial';
3
+ export interface IQuery<Entity = any> extends Omit<FindManyOptions<Entity>, 'where' | 'relations'> {
4
+ where?: FindManyOptions['where'] | string;
5
+ relations?: FindManyOptions['relations'] | string;
6
+ }
7
+ export declare class CrudTypeOrmService<Entity extends ObjectLiteral> {
8
+ protected repo: Repository<Entity>;
9
+ constructor(repo: Repository<Entity>);
10
+ createQuery<Entity>(query?: IQuery<Entity>, primaryKeyValue?: string | number, primaryKeyName?: string): FindManyOptions<Entity>;
11
+ getOne(id: any, query?: any, _req?: Request): Promise<Entity | null>;
12
+ getMany(query?: any, _req?: Request): Promise<{
13
+ data: Entity[];
14
+ count: number;
15
+ }>;
16
+ getManyByRelation(field: string, value: any, query?: any, primaryKeyName?: string, req?: Request): Promise<{
17
+ data: Entity[];
18
+ count: number;
19
+ }>;
20
+ post(body: DeepPartial<Entity>, _query?: any, _req?: Request): Promise<DeepPartial<Entity> & Entity>;
21
+ patchOne(body: DeepPartial<Entity>, primaryKey?: string | number | {
22
+ [key: string]: any;
23
+ }, _req?: Request): Promise<(Entity & {
24
+ id: any;
25
+ }) | ((Entity extends (infer U)[] ? DeepPartial<U>[] : Entity extends Map<infer K, infer V> ? Map<DeepPartial<K>, DeepPartial<V>> : Entity extends Set<infer M> ? Set<DeepPartial<M>> : Entity extends object ? { [K_1 in keyof Entity]?: DeepPartial<Entity[K_1]> | undefined; } : Entity) & {
26
+ id: any;
27
+ } & Entity)>;
28
+ patchMany(body: DeepPartial<Entity>, query?: any, _req?: Request): Promise<((Entity & {
29
+ id: any;
30
+ }) | ((Entity extends (infer U)[] ? DeepPartial<U>[] : Entity extends Map<infer K, infer V> ? Map<DeepPartial<K>, DeepPartial<V>> : Entity extends Set<infer M> ? Set<DeepPartial<M>> : Entity extends object ? { [K_1 in keyof Entity]?: DeepPartial<Entity[K_1]> | undefined; } : Entity) & {
31
+ id: any;
32
+ } & Entity))[]>;
33
+ putOne(): void;
34
+ putMany(): void;
35
+ deleteOne(primaryKeyOrCondition: string | number | {
36
+ [key: string]: any;
37
+ }, query?: any, req?: Request): Promise<{
38
+ affected: number | null | undefined;
39
+ }>;
40
+ deleteMany(query?: any, _req?: Request): Promise<{
41
+ affected: number | null | undefined;
42
+ }>;
43
+ clear(): Promise<void>;
44
+ toArray(value?: any, delimiter?: string): any;
45
+ toObject(value?: any, defaultValue?: any): any;
46
+ }
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "CrudTypeOrmService", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return CrudTypeOrmService;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _typeorm = require("typeorm");
13
+ const _mergeanything = require("merge-anything");
14
+ let CrudTypeOrmService = class CrudTypeOrmService {
15
+ /**
16
+ * create a TypeORM Query object
17
+ * @param query
18
+ * @param primaryKey the primary key value, for "*One" operations
19
+ * @param primaryKeyName the primary key field name, default: "id"
20
+ */ // todo: enforce the return type
21
+ // todo: `T extends FindOneOptions | FindManyOptions | ...`
22
+ createQuery(query, primaryKeyValue, primaryKeyName = 'id') {
23
+ query = {
24
+ ...query,
25
+ // for "one" operations, include deleted records by default
26
+ withDeleted: primaryKeyValue ? query?.withDeleted !== false : query?.withDeleted,
27
+ // todo: nested relations "users.profiles"
28
+ // todo: relation fields "users:name,email"
29
+ // todo: relations may be an object rather than an array
30
+ relations: this.toArray(query?.relations),
31
+ select: this.toArray(query?.select),
32
+ order: this.toObject(query?.order, 'ASC')
33
+ };
34
+ if (query.where) {
35
+ // convert query.where to object
36
+ if (typeof query.where === 'string' && // json format
37
+ (query.where.startsWith('{') || // query.where can be a string, i.e. "primaryKey"
38
+ // if its format is not `key:value, key2` keep it string
39
+ // if it is a single text value, consider it as primaryKey value
40
+ // matches: `a:1, b:2, c` and `a,b` but not `id`
41
+ // todo: deprecate inline k-v syntax, use explicit object
42
+ // eslint-disable-next-line regexp/optimal-quantifier-concatenation
43
+ /(.+:.+(,.+)?)|(.+,.+)/.test(query.where))) {
44
+ query.where = this.toObject(query.where);
45
+ }
46
+ // todo: if isObject()
47
+ if (typeof query.where !== 'string' && !Array.isArray(query.where)) {
48
+ Object.keys(query.where).map((key)=>{
49
+ // @ts-ignore
50
+ let value = query.where[key];
51
+ if (Array.isArray(value)) {
52
+ // check if the array column contains the value
53
+ // todo: use the entity to check if the field type is an array
54
+ // @ts-ignore
55
+ query.where[key] = (0, _typeorm.ArrayContains)(value.map((el)=>el?.trim()));
56
+ } else if (typeof value === 'string') {
57
+ value = value?.trim();
58
+ // if the value starts with ">", consider it as a raw sql statement
59
+ // convert `{key: value}` to `{key: Raw(field=>value)}`
60
+ // to enable sql syntax and find operations
61
+ // such as: `Like '%test%'`
62
+ // todo: check for sql injection of the user's input
63
+ // todo: if it includes a find operation such as `Like` (can be a part of the value)
64
+ // example `{key: "> {field} > 0"}`
65
+ if (value?.startsWith?.('>')) {
66
+ // @ts-ignore
67
+ query.where[key] = (0, _typeorm.Raw)((field)=>`${value.slice(1)}`.replaceAll('{field}', field));
68
+ }
69
+ }
70
+ });
71
+ // add primaryKey to the condition
72
+ if (primaryKeyValue && primaryKeyName) {
73
+ query.where = {
74
+ [primaryKeyName]: primaryKeyValue,
75
+ ...query.where
76
+ };
77
+ }
78
+ }
79
+ } else if (primaryKeyValue) {
80
+ query.where = {
81
+ [primaryKeyName]: primaryKeyValue
82
+ };
83
+ }
84
+ return query;
85
+ }
86
+ // todo: rename to getOneById() or getOneBy${primaryParam}
87
+ // todo: route.paramKey{name, type:String|Number}
88
+ // todo: types
89
+ // todo: parse query params, and add type ControllerOptions.QueryParams
90
+ getOne(id, query, _req) {
91
+ return this.repo.findOne(this.createQuery(query, id));
92
+ }
93
+ // todo: handle operations, example: `{where: {email: "like %john%"} }`
94
+ getMany(query, _req) {
95
+ return this.repo.findAndCount(this.createQuery(query)).then((res)=>({
96
+ data: res[0],
97
+ count: res[1]
98
+ }));
99
+ }
100
+ /**
101
+ * get records by a relation field
102
+ * @param field the relationship field name
103
+ * @param value
104
+ * @param query
105
+ * @param primaryKeyName
106
+ * @param req
107
+ * @returns
108
+ * @example
109
+ * if the table posts has a relation with users,
110
+ * we can get all posts by the user by `getManyByRelation("user", 1)`
111
+ */ // todo: merge options.query{}
112
+ // for example the following route should inherit options from @Controller()
113
+ // @Controller(options)
114
+ // class MyController{
115
+ // @Get('/users/:userId/posts')
116
+ // getUserPosts(){
117
+ // return this.service.getManyByRelation(...) // options.query from @Controller() should be merged here
118
+ // }
119
+ // }
120
+ getManyByRelation(field, value, query, primaryKeyName = 'id', req) {
121
+ return this.getMany((0, _mergeanything.merge)(query || {}, {
122
+ where: {
123
+ [field]: {
124
+ [primaryKeyName]: value
125
+ }
126
+ }
127
+ }), req);
128
+ }
129
+ post(body, _query, _req) {
130
+ // .save() automatically saves the manyToMany relations if cascade=true
131
+ // or this.repo.insert(body).then((res) => (Array.isArray(body) ? res?.raw : res?.raw[0]))
132
+ return this.repo.save(body);
133
+ }
134
+ /**
135
+ * update a record by its primary key
136
+ */ patchOne(// todo: add type opts.model
137
+ body, primaryKey = 'id', _req) {
138
+ return this.getOne(primaryKey).then((res)=>{
139
+ if (!res?.id) throw new Error(`not found`);
140
+ // .save() automatically modifies the ManyToMany relations
141
+ // but requires fetching the items to be modified to add the id of each item
142
+ // otherwise it will create a new item if it doesn't have an id
143
+ return this.repo.save({
144
+ ...body,
145
+ id: res.id
146
+ });
147
+ });
148
+ }
149
+ patchMany(body, query, _req) {
150
+ return this.repo.find(query).then((res)=>res.map((el)=>({
151
+ ...body,
152
+ id: el.id
153
+ }))).then((res)=>this.repo.save(res));
154
+ // .update() doesn't save ManyToMany relation automatically
155
+ // but .save() doesn't have where condition (each item has to specify id)
156
+ // and has no limit option (it relies on the number of the items being updated)
157
+ // use .save() see patchOne(), it meeds merging body[] with existing ids of the items to be updated
158
+ // or create a route to save (insert or update) items `POST /save`
159
+ // return this.repo
160
+ // .update(query.where, body)
161
+ // .then((res) => ({ affected: res.affected }));
162
+ }
163
+ putOne() {
164
+ throw new _common.NotImplementedException();
165
+ }
166
+ putMany() {
167
+ throw new _common.NotImplementedException();
168
+ }
169
+ deleteOne(primaryKeyOrCondition, query = {}, req) {
170
+ query.where = [
171
+ 'string',
172
+ 'number'
173
+ ].includes(typeof primaryKeyOrCondition) ? primaryKeyOrCondition : {
174
+ ...query.where,
175
+ ...primaryKeyOrCondition
176
+ };
177
+ return this.deleteMany(query, req);
178
+ }
179
+ deleteMany(query, _req) {
180
+ // todo: softDelete() vs softRemove()
181
+ let deleteQuery;
182
+ // repo.delete({}) no longer allowed by TypeORM, in this case we'll use QueryBuilder to provide a fake criteria
183
+ if (query.where) {
184
+ deleteQuery = query.softDelete === false ? this.repo.delete(query.where) : this.repo.softDelete(query.where);
185
+ } else {
186
+ let builder = this.repo.createQueryBuilder();
187
+ deleteQuery = query.softDelete === false ? builder.delete().where('1 = 1').execute() : builder.softDelete().where('1 = 1').execute();
188
+ }
189
+ return deleteQuery.then((res)=>({
190
+ affected: res?.affected
191
+ }));
192
+ }
193
+ /**
194
+ * delete all data using `TRUNCATE` instead of `DELETE`
195
+ */ // todo: add a route to clear (truncate) the data, for instance `DELETE <entity>/clear`
196
+ clear() {
197
+ return this.repo.clear();
198
+ }
199
+ /**
200
+ * convert delimited string int array items and remove empty items
201
+ * @example 'a,b,c' => ['a', 'b', 'c']
202
+ */ toArray(value, delimiter = ',') {
203
+ return typeof value === 'string' ? value?.split?.(delimiter)?.filter((el)=>el && el.trim() !== '') : value;
204
+ }
205
+ /**
206
+ * convert JSON string or delimited string into a plain object
207
+ * @example `{"a": 1}` => `{a: 1}`
208
+ * @example `a:1, b:2, c` => `{a: 1, b: 2, c: defaultValue}`
209
+ */ toObject(value, defaultValue = true) {
210
+ if (!value) return;
211
+ if (typeof value !== 'string') return value;
212
+ // todo: if isObject(value) return value
213
+ try {
214
+ return JSON.parse(value);
215
+ } catch {
216
+ return value?.split?.(',')?.reduce?.((acc, el)=>{
217
+ let [key, value = defaultValue] = el.split(':');
218
+ return {
219
+ ...acc,
220
+ [key.trim()]: value.trim()
221
+ };
222
+ }, {});
223
+ }
224
+ }
225
+ constructor(// todo: inject repo using Entity
226
+ // @InjectRepository(Dto) private repo: Repository<Entity>,
227
+ repo){
228
+ this.repo = repo;
229
+ }
230
+ };
231
+
232
+ //# sourceMappingURL=crud-typeorm.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/services/crud-typeorm.service.ts"],"sourcesContent":["import { NotImplementedException } from '@nestjs/common';\nimport {\n ArrayContains,\n DeleteResult,\n type FindManyOptions,\n type ObjectLiteral,\n Raw,\n Repository,\n UpdateResult,\n} from 'typeorm';\nimport type { DeepPartial } from 'typeorm/common/DeepPartial';\n// @ts-ignore\nimport { merge } from 'merge-anything';\n\n/**\n * find options but some props accepts string format that parsed internally into the correct format\n * for example { where: \"age>30,name='john'\" }\n */\nexport interface IQuery<Entity = any> extends Omit<\n FindManyOptions<Entity>,\n 'where' | 'relations'\n> {\n where?: FindManyOptions['where'] | string;\n relations?: FindManyOptions['relations'] | string;\n}\n\n/**\n * The CRUD implementation service for TypeORM\n * it adds methods such as `getOne()`, `getMany()`, and `post()` that ue TypeORM repo\n * to work with the data model\n */\n// todo: add pipes, example: `update(@Param('id', ParseUUIDPipe) id: string, body){}`\nexport class CrudTypeOrmService<Entity extends ObjectLiteral> {\n constructor(\n // todo: inject repo using Entity\n // @InjectRepository(Dto) private repo: Repository<Entity>,\n protected repo: Repository<Entity>,\n ) {}\n\n /**\n * create a TypeORM Query object\n * @param query\n * @param primaryKey the primary key value, for \"*One\" operations\n * @param primaryKeyName the primary key field name, default: \"id\"\n */\n // todo: enforce the return type\n // todo: `T extends FindOneOptions | FindManyOptions | ...`\n createQuery<Entity>(\n query?: IQuery<Entity>,\n primaryKeyValue?: string | number,\n primaryKeyName = 'id', // FindManyOptions already extends FindOneOptions\n ): FindManyOptions<Entity> {\n query = {\n ...query,\n // for \"one\" operations, include deleted records by default\n withDeleted: primaryKeyValue\n ? query?.withDeleted !== false\n : query?.withDeleted,\n // todo: nested relations \"users.profiles\"\n // todo: relation fields \"users:name,email\"\n // todo: relations may be an object rather than an array\n relations: this.toArray(query?.relations),\n select: this.toArray(query?.select),\n order: this.toObject(query?.order, 'ASC'),\n };\n\n if (query.where) {\n // convert query.where to object\n if (\n typeof query.where === 'string' &&\n // json format\n (query.where.startsWith('{') ||\n // query.where can be a string, i.e. \"primaryKey\"\n // if its format is not `key:value, key2` keep it string\n // if it is a single text value, consider it as primaryKey value\n // matches: `a:1, b:2, c` and `a,b` but not `id`\n // todo: deprecate inline k-v syntax, use explicit object\n // eslint-disable-next-line regexp/optimal-quantifier-concatenation\n /(.+:.+(,.+)?)|(.+,.+)/.test(query.where))\n ) {\n query.where = this.toObject(query.where);\n }\n\n // todo: if isObject()\n if (typeof query.where !== 'string' && !Array.isArray(query.where)) {\n Object.keys(query.where!).map((key) => {\n // @ts-ignore\n let value = query.where[key];\n if (Array.isArray(value)) {\n // check if the array column contains the value\n // todo: use the entity to check if the field type is an array\n // @ts-ignore\n query.where[key] = ArrayContains(value.map((el) => el?.trim()));\n } else if (typeof value === 'string') {\n value = value?.trim();\n\n // if the value starts with \">\", consider it as a raw sql statement\n // convert `{key: value}` to `{key: Raw(field=>value)}`\n // to enable sql syntax and find operations\n // such as: `Like '%test%'`\n // todo: check for sql injection of the user's input\n // todo: if it includes a find operation such as `Like` (can be a part of the value)\n // example `{key: \"> {field} > 0\"}`\n if (value?.startsWith?.('>')) {\n // @ts-ignore\n query.where[key] = Raw((field: string) =>\n `${value.slice(1)}`.replaceAll('{field}', field),\n );\n }\n }\n });\n\n // add primaryKey to the condition\n if (primaryKeyValue && primaryKeyName) {\n query.where = {\n [primaryKeyName]: primaryKeyValue,\n ...query.where,\n };\n }\n }\n } else if (primaryKeyValue) {\n query.where = { [primaryKeyName]: primaryKeyValue };\n }\n\n return <FindManyOptions<Entity>>query;\n }\n\n // todo: rename to getOneById() or getOneBy${primaryParam}\n // todo: route.paramKey{name, type:String|Number}\n // todo: types\n // todo: parse query params, and add type ControllerOptions.QueryParams\n getOne(id: any, query?: any, _req?: Request) {\n return this.repo.findOne(this.createQuery(query, id));\n }\n\n // todo: handle operations, example: `{where: {email: \"like %john%\"} }`\n getMany(query?: any, _req?: Request) {\n return this.repo\n .findAndCount(this.createQuery(query))\n .then((res) => ({ data: res[0], count: res[1] }));\n }\n\n /**\n * get records by a relation field\n * @param field the relationship field name\n * @param value\n * @param query\n * @param primaryKeyName\n * @param req\n * @returns\n * @example\n * if the table posts has a relation with users,\n * we can get all posts by the user by `getManyByRelation(\"user\", 1)`\n */\n // todo: merge options.query{}\n // for example the following route should inherit options from @Controller()\n // @Controller(options)\n // class MyController{\n // @Get('/users/:userId/posts')\n // getUserPosts(){\n // return this.service.getManyByRelation(...) // options.query from @Controller() should be merged here\n // }\n // }\n getManyByRelation(\n field: string,\n value: any,\n query?: any,\n primaryKeyName = 'id',\n req?: Request,\n ) {\n return this.getMany(\n merge(query || {}, { where: { [field]: { [primaryKeyName]: value } } }),\n req,\n );\n }\n\n post(body: DeepPartial<Entity>, _query?: any, _req?: Request) {\n // .save() automatically saves the manyToMany relations if cascade=true\n // or this.repo.insert(body).then((res) => (Array.isArray(body) ? res?.raw : res?.raw[0]))\n return this.repo.save(body);\n }\n\n /**\n * update a record by its primary key\n */\n patchOne(\n // todo: add type opts.model\n body: DeepPartial<Entity>,\n primaryKey: string | number | { [key: string]: any } = 'id',\n _req?: Request,\n ) {\n return this.getOne(primaryKey).then((res) => {\n if (!res?.id) throw new Error(`not found`);\n // .save() automatically modifies the ManyToMany relations\n // but requires fetching the items to be modified to add the id of each item\n // otherwise it will create a new item if it doesn't have an id\n return this.repo.save({ ...body, id: res.id });\n });\n }\n\n patchMany(body: DeepPartial<Entity>, query?: any, _req?: Request) {\n return this.repo\n .find(query)\n .then((res) => res.map((el) => ({ ...body, id: el.id })))\n .then((res) => this.repo.save(res));\n\n // .update() doesn't save ManyToMany relation automatically\n // but .save() doesn't have where condition (each item has to specify id)\n // and has no limit option (it relies on the number of the items being updated)\n // use .save() see patchOne(), it meeds merging body[] with existing ids of the items to be updated\n // or create a route to save (insert or update) items `POST /save`\n\n // return this.repo\n // .update(query.where, body)\n // .then((res) => ({ affected: res.affected }));\n }\n\n putOne() {\n throw new NotImplementedException();\n }\n\n putMany() {\n throw new NotImplementedException();\n }\n\n deleteOne(\n primaryKeyOrCondition: string | number | { [key: string]: any },\n query: any = {},\n req?: Request,\n ) {\n query.where = ['string', 'number'].includes(typeof primaryKeyOrCondition)\n ? primaryKeyOrCondition\n : { ...query.where, ...(<{ [key: string]: any }>primaryKeyOrCondition) };\n return this.deleteMany(query, req);\n }\n\n deleteMany(query?: any, _req?: Request) {\n // todo: softDelete() vs softRemove()\n\n let deleteQuery: Promise<DeleteResult | UpdateResult>;\n // repo.delete({}) no longer allowed by TypeORM, in this case we'll use QueryBuilder to provide a fake criteria\n if (query.where) {\n deleteQuery =\n query.softDelete === false\n ? this.repo.delete(query.where)\n : this.repo.softDelete(query.where);\n } else {\n let builder = this.repo.createQueryBuilder();\n deleteQuery =\n query.softDelete === false\n ? builder.delete().where('1 = 1').execute()\n : builder.softDelete().where('1 = 1').execute();\n }\n return deleteQuery.then((res) => ({ affected: res?.affected }));\n }\n\n /**\n * delete all data using `TRUNCATE` instead of `DELETE`\n */\n // todo: add a route to clear (truncate) the data, for instance `DELETE <entity>/clear`\n clear() {\n return this.repo.clear();\n }\n\n /**\n * convert delimited string int array items and remove empty items\n * @example 'a,b,c' => ['a', 'b', 'c']\n */\n toArray(value?: any, delimiter = ',') {\n return typeof value === 'string'\n ? value\n ?.split?.(delimiter)\n // remove empty elements\n ?.filter((el) => el && el.trim() !== '')\n : value;\n }\n\n /**\n * convert JSON string or delimited string into a plain object\n * @example `{\"a\": 1}` => `{a: 1}`\n * @example `a:1, b:2, c` => `{a: 1, b: 2, c: defaultValue}`\n */\n toObject(value?: any, defaultValue: any = true) {\n if (!value) return;\n if (typeof value !== 'string') return value;\n // todo: if isObject(value) return value\n\n try {\n return JSON.parse(value);\n } catch {\n return value?.split?.(',')?.reduce?.(\n (acc, el) => {\n let [key, value = defaultValue] = el.split(':');\n return { ...acc, [key.trim()]: value.trim() };\n },\n <{ [key: string]: any }>{},\n );\n }\n }\n}\n"],"names":["CrudTypeOrmService","createQuery","query","primaryKeyValue","primaryKeyName","withDeleted","relations","toArray","select","order","toObject","where","startsWith","test","Array","isArray","Object","keys","map","key","value","ArrayContains","el","trim","Raw","field","slice","replaceAll","getOne","id","_req","repo","findOne","getMany","findAndCount","then","res","data","count","getManyByRelation","req","merge","post","body","_query","save","patchOne","primaryKey","Error","patchMany","find","putOne","NotImplementedException","putMany","deleteOne","primaryKeyOrCondition","includes","deleteMany","deleteQuery","softDelete","delete","builder","createQueryBuilder","execute","affected","clear","delimiter","split","filter","defaultValue","JSON","parse","reduce","acc"],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { HttpStatus, type NestInterceptor, type Type } from '@nestjs/common';
2
+ import type { ApiOperationOptions, ApiParamOptions } from '@nestjs/swagger';
3
+ import type { ParameterObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
4
+ import type { SwaggerEnumType } from '@nestjs/swagger/dist/types/swagger-enum.type';
5
+ type MakeRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
6
+ export interface ControllerOptions extends MakeRequired<InitControllerOptions, 'serviceName' | 'routes'> {
7
+ routes: IRoute[];
8
+ }
9
+ export interface IRoute extends MakeRequired<InitRoute, 'methodName' | 'httpMethod' | 'decorators' | 'interceptors'> {
10
+ }
11
+ export interface InitControllerOptions {
12
+ path?: string;
13
+ routes?: InitRoute[];
14
+ queryParams?: QueryParam[];
15
+ serviceName?: string;
16
+ model?: any;
17
+ responseModel?: InitControllerOptions['model'];
18
+ errorResponseModel?: InitControllerOptions['model'];
19
+ responseModels?: Array<{
20
+ status: HttpStatus;
21
+ type: InitControllerOptions['model'];
22
+ }>;
23
+ primaryKey?: string;
24
+ maxLimit?: number;
25
+ decorators?: (MethodDecorator | PropertyDecorator)[];
26
+ interceptors?: (NestInterceptor | Function)[];
27
+ query?: {
28
+ [key: string]: any;
29
+ };
30
+ tags?: string | string | null;
31
+ }
32
+ export interface InitRoute extends ApiOperationOptions, Omit<InitControllerOptions, 'routes' | 'tags'> {
33
+ httpMethod?: HttpMethod;
34
+ path?: string;
35
+ methodName?: string;
36
+ many?: boolean;
37
+ methodFactory?: () => any;
38
+ description?: string;
39
+ pathParams?: ApiParamOptions[];
40
+ disabled?: boolean;
41
+ }
42
+ export interface QueryParam extends ParameterOptions {
43
+ name?: string;
44
+ type?: Type<unknown> | Function | [Function] | string;
45
+ isArray?: boolean;
46
+ enum?: SwaggerEnumType;
47
+ enumName?: string;
48
+ many?: boolean;
49
+ httpMethods?: HttpMethod[];
50
+ }
51
+ type ParameterOptions = Omit<ParameterObject, 'in' | 'name'>;
52
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
53
+ export type RouteHandler = (req: Request, query: any, ...params: any[]) => any;
54
+ export {};
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ import { ControllerOptions, InitControllerOptions } from '../types';
2
+ export declare function mergeOptions(...options: InitControllerOptions[]): ControllerOptions;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "mergeOptions", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return mergeOptions;
9
+ }
10
+ });
11
+ const _javascript = require("@impactor/javascript");
12
+ function mergeOptions(...options) {
13
+ let opts = (0, _javascript.merge)(options.map((el)=>(0, _javascript.removeValues)(el, [
14
+ undefined,
15
+ null
16
+ ]) || {}), {
17
+ deep: true
18
+ });
19
+ // todo: merge matched routes (by methodNam, path, and httpMethod) and queryParams (by name)
20
+ // the later override the previous one
21
+ // opts.queryParams?.map((param) => {
22
+ // let existingQueryParam = opts.queryParams!.findIndex(
23
+ // (el) => el.name === param.name,
24
+ // );
25
+ // opts.queryParams!.push(
26
+ // existingQueryParam > -1
27
+ // ? {
28
+ // ...opts.queryParams![existingQueryParam],
29
+ // ...param,
30
+ // }
31
+ // : param,
32
+ // );
33
+ // });
34
+ return opts;
35
+ }
36
+
37
+ //# sourceMappingURL=merge-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/utils/merge-options.ts"],"sourcesContent":["import { ControllerOptions, InitControllerOptions } from '../types';\nimport { merge, removeValues } from '@impactor/javascript';\n\nexport function mergeOptions(\n ...options: InitControllerOptions[]\n): ControllerOptions {\n let opts = <ControllerOptions>merge(\n options.map((el) => removeValues(el, [undefined, null]) || {}),\n { deep: true },\n );\n\n // todo: merge matched routes (by methodNam, path, and httpMethod) and queryParams (by name)\n // the later override the previous one\n // opts.queryParams?.map((param) => {\n // let existingQueryParam = opts.queryParams!.findIndex(\n // (el) => el.name === param.name,\n // );\n\n // opts.queryParams!.push(\n // existingQueryParam > -1\n // ? {\n // ...opts.queryParams![existingQueryParam],\n // ...param,\n // }\n // : param,\n // );\n // });\n\n return opts;\n}\n"],"names":["mergeOptions","options","opts","merge","map","el","removeValues","undefined","deep"],"mappings":";;;;+BAGgBA;;;eAAAA;;;4BAFoB;AAE7B,SAASA,aACd,GAAGC,OAAgC;IAEnC,IAAIC,OAA0BC,IAAAA,iBAAK,EACjCF,QAAQG,GAAG,CAAC,CAACC,KAAOC,IAAAA,wBAAY,EAACD,IAAI;YAACE;YAAW;SAAK,KAAK,CAAC,IAC5D;QAAEC,MAAM;IAAK;IAGf,4FAA4F;IAC5F,sCAAsC;IACtC,qCAAqC;IACrC,0DAA0D;IAC1D,sCAAsC;IACtC,OAAO;IAEP,4BAA4B;IAC5B,8BAA8B;IAC9B,YAAY;IACZ,sDAAsD;IACtD,sBAAsB;IACtB,YAAY;IACZ,iBAAiB;IACjB,OAAO;IACP,MAAM;IAEN,OAAON;AACT"}
@@ -0,0 +1,10 @@
1
+ import { ControllerFactory } from '../controller.factory';
2
+ import { IRoute } from '../types';
3
+ export declare function has(property: string, factory: ControllerFactory, route?: IRoute): boolean;
4
+ export declare function get(property: string, factory: ControllerFactory, route?: IRoute): any;
5
+ export declare function set<T extends Object>(property: string, value: T, factory: ControllerFactory, route?: IRoute): void;
6
+ export declare function getKeys(factory: ControllerFactory, route?: IRoute): any[];
7
+ export declare function getAll(factory: ControllerFactory, route?: IRoute): {
8
+ [key: string]: unknown;
9
+ };
10
+ export declare function applyDecorators(factory: ControllerFactory): void;