@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,3 @@
1
+ import { ControllerFactory } from '../controller.factory';
2
+ import type { IRoute } from '../types';
3
+ export declare function addSwaggerResponse(route: IRoute, factory: ControllerFactory): void;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "addSwaggerResponse", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return addSwaggerResponse;
9
+ }
10
+ });
11
+ const _reflect = require("../utils/reflect");
12
+ const _common = require("@nestjs/common");
13
+ const _swagger = require("@nestjs/swagger");
14
+ function addSwaggerResponse(route, factory) {
15
+ if ((0, _reflect.has)('swagger/apiResponse', factory, route)) return;
16
+ let responseModel = route.responseModel || route.model || factory.options?.model;
17
+ // todo: add other status responses
18
+ // todo: add options.responseModels[status] -> example: response models for 200, 401, 500, ...
19
+ route.decorators.push((0, _swagger.ApiResponse)({
20
+ status: _common.HttpStatus.OK,
21
+ type: route.many ? [
22
+ responseModel
23
+ ] : responseModel
24
+ }), (0, _swagger.ApiResponse)({
25
+ status: _common.HttpStatus.INTERNAL_SERVER_ERROR,
26
+ type: route.errorResponseModel || factory.options?.errorResponseModel
27
+ }), ...[
28
+ ...factory.options?.responseModels || [],
29
+ ...route.responseModels || []
30
+ ].map((model)=>(0, _swagger.ApiResponse)(model)));
31
+ }
32
+
33
+ //# sourceMappingURL=add-swagger-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/add-decorators/add-swagger-response.ts"],"sourcesContent":["import { has } from '../utils/reflect';\nimport { ControllerFactory } from '../controller.factory';\nimport type { IRoute } from '../types';\nimport { HttpStatus } from '@nestjs/common';\nimport { ApiResponse } from '@nestjs/swagger';\n\nexport function addSwaggerResponse(route: IRoute, factory: ControllerFactory) {\n if (has('swagger/apiResponse', factory, route)) return;\n\n let responseModel =\n route.responseModel || route.model || factory.options?.model;\n\n // todo: add other status responses\n // todo: add options.responseModels[status] -> example: response models for 200, 401, 500, ...\n route.decorators.push(\n ApiResponse({\n status: HttpStatus.OK,\n type: route.many ? [responseModel] : responseModel,\n }),\n ApiResponse({\n status: HttpStatus.INTERNAL_SERVER_ERROR,\n type: route.errorResponseModel || factory.options?.errorResponseModel,\n }),\n ...[\n ...(factory.options?.responseModels || []),\n ...(route.responseModels || []),\n ].map((model) => ApiResponse(model)),\n );\n}\n"],"names":["addSwaggerResponse","route","factory","has","responseModel","model","options","decorators","push","ApiResponse","status","HttpStatus","OK","type","many","INTERNAL_SERVER_ERROR","errorResponseModel","responseModels","map"],"mappings":";;;;+BAMgBA;;;eAAAA;;;yBANI;wBAGO;yBACC;AAErB,SAASA,mBAAmBC,KAAa,EAAEC,OAA0B;IAC1E,IAAIC,IAAAA,YAAG,EAAC,uBAAuBD,SAASD,QAAQ;IAEhD,IAAIG,gBACFH,MAAMG,aAAa,IAAIH,MAAMI,KAAK,IAAIH,QAAQI,OAAO,EAAED;IAEzD,mCAAmC;IACnC,8FAA8F;IAC9FJ,MAAMM,UAAU,CAACC,IAAI,CACnBC,IAAAA,oBAAW,EAAC;QACVC,QAAQC,kBAAU,CAACC,EAAE;QACrBC,MAAMZ,MAAMa,IAAI,GAAG;YAACV;SAAc,GAAGA;IACvC,IACAK,IAAAA,oBAAW,EAAC;QACVC,QAAQC,kBAAU,CAACI,qBAAqB;QACxCF,MAAMZ,MAAMe,kBAAkB,IAAId,QAAQI,OAAO,EAAEU;IACrD,OACG;WACGd,QAAQI,OAAO,EAAEW,kBAAkB,EAAE;WACrChB,MAAMgB,cAAc,IAAI,EAAE;KAC/B,CAACC,GAAG,CAAC,CAACb,QAAUI,IAAAA,oBAAW,EAACJ;AAEjC"}
@@ -0,0 +1,2 @@
1
+ import type { ControllerOptions, InitControllerOptions } from './types';
2
+ export declare function Controller(options?: InitControllerOptions | ((defaultOptions: ControllerOptions) => ControllerOptions)): ClassDecorator;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "Controller", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return Controller;
9
+ }
10
+ });
11
+ const _controllerfactory = require("./controller.factory");
12
+ function Controller(options) {
13
+ return (target)=>{
14
+ new _controllerfactory.ControllerFactory(target, options);
15
+ };
16
+ }
17
+
18
+ //# sourceMappingURL=controller.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/decorators/controller/controller.decorator.ts"],"sourcesContent":["import { ControllerFactory } from './controller.factory';\nimport type { ControllerOptions, InitControllerOptions } from './types';\n\n/**\n * Mark a class as a controller and automatically generate its crud routes, and add swagger decorators\n * This decorator must be placed before other decorators to avoid overriding their values\n * otherwise, you can disable any functionality by setting it to `null`\n * for instance `@ApiTags(\"users\"); @Controller(\"my-users\")`\n * may cause that the value of `ApiTags` to be replaced\n * either place `@Controller()` first, or use `@Controller({tags: null})`\n * @param options\n * @returns\n */\nexport function Controller(\n options?:\n | InitControllerOptions\n | ((defaultOptions: ControllerOptions) => ControllerOptions),\n): ClassDecorator {\n return (target: Function) => {\n new ControllerFactory(target, options);\n };\n}\n"],"names":["Controller","options","target","ControllerFactory"],"mappings":";;;;+BAagBA;;;eAAAA;;;mCAbkB;AAa3B,SAASA,WACdC,OAE8D;IAE9D,OAAO,CAACC;QACN,IAAIC,oCAAiB,CAACD,QAAQD;IAChC;AACF"}
@@ -0,0 +1,11 @@
1
+ import type { ControllerOptions, InitControllerOptions, InitRoute } from './types';
2
+ export declare class ControllerFactory {
3
+ controller: Function;
4
+ options: ControllerOptions;
5
+ decorators: ClassDecorator[];
6
+ constructor(controller: Function, options?: InitControllerOptions | ((defaultOptions: ControllerOptions) => ControllerOptions));
7
+ getExistingMethods(): InitRoute[];
8
+ adjustRoutes(opts?: InitControllerOptions): ControllerOptions;
9
+ createRoutes(): void;
10
+ mergeRoutes(): void;
11
+ }
@@ -0,0 +1,261 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "ControllerFactory", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return ControllerFactory;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _addhttpmethod = require("./add-decorators/add-http-method");
13
+ const _addswaggerparams = require("./add-decorators/add-swagger-params");
14
+ const _routehandler = require("./route-handler");
15
+ const _addswaggeroperation = require("./add-decorators/add-swagger-operation");
16
+ const _addswaggerbody = require("./add-decorators/add-swagger-body");
17
+ const _addswaggerresponse = require("./add-decorators/add-swagger-response");
18
+ const _queryinterceptor = require("./interceptors/query.interceptor");
19
+ const _defaultoptions = require("./default-options");
20
+ const _mergeoptions = require("./utils/merge-options");
21
+ const _addcontrollertags = require("./add-decorators/add-controller-tags");
22
+ const _javascript = require("@impactor/javascript");
23
+ const _reflect = require("./utils/reflect");
24
+ const _addcontrollerpath = require("./add-decorators/add-controller-path");
25
+ let ControllerFactory = class ControllerFactory {
26
+ getExistingMethods() {
27
+ let proto = this.controller.prototype;
28
+ return Reflect.ownKeys(proto).filter((key)=>typeof proto[key] === 'function')// convert symbol to string
29
+ .map(String).filter((key)=>key !== 'constructor' && // a method can be excluded from being handling by this factory, by adding `@Route(false)`
30
+ Reflect.getMetadata('routeOptions', proto, key) !== false && // skip methods that are already included in routes[]
31
+ !this.options.routes?.some((route)=>route.methodName === key))// convert method to InitRoute{}
32
+ .map((methodName)=>{
33
+ let routeOptions = Reflect.getMetadata('routeOptions', proto, methodName);
34
+ return {
35
+ methodName,
36
+ path: Reflect.getMetadata('path', proto[methodName]),
37
+ ...routeOptions
38
+ };
39
+ });
40
+ }
41
+ /**
42
+ * add all missing props to each route, uch as the HTTP method, primaryKey, and operationId .
43
+ * @param opts
44
+ * @returns
45
+ */ adjustRoutes(opts = this.options) {
46
+ return {
47
+ ...opts,
48
+ routes: opts?.routes?.map((el)=>{
49
+ let route = {
50
+ ...el
51
+ };
52
+ // NestJs request methods
53
+ let requestMethods = [
54
+ 'GET',
55
+ 'POST',
56
+ 'PUT',
57
+ 'DELETE',
58
+ 'PATCH',
59
+ 'ALL',
60
+ 'OPTIONS',
61
+ 'HEAD',
62
+ 'SEARCH'
63
+ ];
64
+ // capitalize route.httpMethod to much the HttpMethod type
65
+ route.httpMethod = typeof route.httpMethod === 'number' ? requestMethods[route.httpMethod] : route.httpMethod?.toUpperCase() || 'GET';
66
+ route.primaryKey = route.primaryKey || opts.primaryKey || 'id';
67
+ // generate the default methodName from path
68
+ // examples
69
+ // - {path: "users/:id"} -> "getOneUserById"
70
+ // - {path: "users/:userId/profiles/:profileId/posts", many:true} -> "getManyUsersByUserIdProfilesProfileIdPosts"
71
+ route.methodName = route.methodName || route.httpMethod.toLowerCase() + (route.httpMethod === 'POST' ? '' : route.many ? 'Many' : 'One');
72
+ route.operationId = route.operationId || `${this.controller.name}_${String(route.methodName)}`;
73
+ if (!route.path) {
74
+ // for "*One()" operations, add /:id path param
75
+ // example: GET /users/:id
76
+ route.path = route.many || [
77
+ 'POST'
78
+ ].includes(route.httpMethod) ? '/' : `/:${route.primaryKey}`;
79
+ }
80
+ route.pathParams = route.pathParams ? route.pathParams.map((param)=>typeof param === 'string' ? {
81
+ name: param,
82
+ schema: {
83
+ type: 'string'
84
+ },
85
+ required: true
86
+ } : param) : // matches `:id`, `/:id` and `other/:id/:userId`
87
+ [
88
+ ...route.path.matchAll(/^\/?:([^/]+)/g)
89
+ ].map((match)=>({
90
+ name: match[1],
91
+ schema: {
92
+ type: 'string'
93
+ },
94
+ required: true
95
+ }));
96
+ // todo: generate the corresponding service methods
97
+ // +route.path
98
+ // // remove the leading "/"
99
+ // .replace(/^\//, '')
100
+ // .replace(/^:/,'By')
101
+ // // replace "/:id" with "ById"
102
+ // .replaceAll(/\/:/g, '/By')
103
+ // .split('/')
104
+ // // convert parts to camel case
105
+ // // example: "users/profiles" -> "usersProfiles"
106
+ // .map((el, idx) =>
107
+ // idx === 0 ? el : el.charAt(0).toUpperCase() + el.slice(1),
108
+ // )
109
+ // .join('');
110
+ // override options per route
111
+ // for example: route.primaryKey = route.primaryKey || opts.primaryKey
112
+ // when opts.model is a class that extends another class, an exception is thrown `OtherClass is not defined`
113
+ // todo: we may need to exclude other keys that have a class value such as errorResponseModel
114
+ let { routes, model, ...otherOptions } = opts;
115
+ // todo: use merge()?
116
+ route = (0, _javascript.merge)([
117
+ otherOptions || {},
118
+ route || {}
119
+ ].map((el)=>(0, _javascript.removeValues)(el, [
120
+ undefined,
121
+ null
122
+ ])));
123
+ // merge arrays
124
+ // todo: use mergeArray()?
125
+ [
126
+ 'decorators',
127
+ 'interceptors'
128
+ ].map((key)=>{
129
+ route[key] = [
130
+ ...opts[key] || [],
131
+ ...route[key] || []
132
+ ];
133
+ });
134
+ // merge empty arrays
135
+ // for example if route.interceptors=[] but opts.interceptors has values, opts.interceptors is used
136
+ // Object.keys(opts).map((key) => {
137
+ // if (Array.isArray(key) && !route[key as keyof typeof route]?.length) {
138
+ // route[key as keyof typeof route] =
139
+ // opts[key as keyof typeof opts] || [];
140
+ // }
141
+ // });
142
+ route.interceptors.push(new _queryinterceptor.QueryInterceptor((0, _javascript.merge)([
143
+ opts.query || {},
144
+ route.query || {}
145
+ ])));
146
+ return route;
147
+ })
148
+ };
149
+ }
150
+ /**
151
+ * add options.routes[] as class methods
152
+ */ createRoutes() {
153
+ let routes = this.options.routes?.filter((route)=>route.disabled === undefined || !route.disabled), controller = this.controller.prototype;
154
+ for (let route of routes){
155
+ // create a route method and add it to the controller
156
+ // todo: use interceptor to create ParsedRequest
157
+ if (!controller[route.methodName]) {
158
+ controller[route.methodName] = (0, _routehandler.routeHandler)(route, this);
159
+ // inject the required params
160
+ // similar to patchOne(@Req() req: Request, @Query() query, @Body() dto: MyEntity, @Param("id") id:string)
161
+ // we can get other info such as body and params from req
162
+ // without the need to inject @Body() and @Query()
163
+ // todo: route.primary || getPrimaryKeyFromEntity(opts.model) || "id"
164
+ let paramKey = route.primaryKey || 'id';
165
+ (0, _common.Req)()(this.controller.prototype, route.methodName, 0);
166
+ (0, _common.Query)()(this.controller.prototype, route.methodName, 1);
167
+ if ([
168
+ 'GET',
169
+ 'DELETE'
170
+ ].includes(route.httpMethod) && !route.many) {
171
+ (0, _common.Param)(paramKey)(this.controller.prototype, route.methodName, 2);
172
+ } else if (route.httpMethod === 'POST') {
173
+ (0, _common.Body)()(this.controller.prototype, route.methodName, 2);
174
+ // add 'design:paramtypes' to define the types of each param
175
+ // for instance, the param `@Body()` should be defined as `this.options.model`,
176
+ // so `ValidationPipe()` can work fine.
177
+ // without it, wrong body will bypass class-validator and class-transformer decorators such as `@IsString()`
178
+ Reflect.defineMetadata('design:paramtypes', // 0: `@Req() req: Request`
179
+ // 1: `@Query() query: any` -> todo: add type definition
180
+ // 2: `@Body() body: Model`
181
+ [
182
+ Request,
183
+ Object,
184
+ this.options.model
185
+ ], this.controller.prototype, route.methodName);
186
+ } else if ([
187
+ 'PATCH',
188
+ 'PUT'
189
+ ].includes(route.httpMethod)) {
190
+ (0, _common.Body)()(this.controller.prototype, route.methodName, 2);
191
+ if (route.many) {
192
+ Reflect.defineMetadata('design:paramtypes', [
193
+ Object,
194
+ Object,
195
+ this.options.model
196
+ ], this.controller.prototype, route.methodName);
197
+ } else {
198
+ // todo: or use `Param()` without `paramKey`, then use it like param[paramKey]
199
+ // todo: accept list of pipes for each param -> `Param(paramName, ...pipes)(...)`
200
+ // to accept only string or number -> add a validation in routerHandler i.e. `if(! typeof params[paramKey]) throw ...`,
201
+ // as class-validator will not validate its type because it is not a DTO i.e. `getOne(@param() params: ParamsDTO)`
202
+ (0, _common.Param)(paramKey)(this.controller.prototype, route.methodName, 3);
203
+ Reflect.defineMetadata('design:paramtypes', [
204
+ Object,
205
+ Object,
206
+ this.options.model,
207
+ Object
208
+ ], this.controller.prototype, route.methodName);
209
+ }
210
+ }
211
+ }
212
+ // add decorators to the route
213
+ // annotations are added only if:
214
+ // - the method listed in this.options.routes[] and doesn't has @Route(false)
215
+ // - the method is not listed and has @Route(options?)
216
+ // if the method is not listed and doesn't has @Route(), the annotations will not be added
217
+ // if no http decorator already applied such as @GET()
218
+ // when adding @Get(), nest adds "method" and "path" metadata
219
+ (0, _addhttpmethod.addHttpMethod)(route, this);
220
+ (0, _addswaggeroperation.addSwaggerOperation)(route, this);
221
+ (0, _addswaggerparams.addSwaggerParams)(route, this);
222
+ (0, _addswaggerbody.addSwaggerBody)(route, this);
223
+ (0, _addswaggerresponse.addSwaggerResponse)(route, this);
224
+ }
225
+ }
226
+ /**
227
+ * Merges routes that share the same methodName.
228
+ */ mergeRoutes() {
229
+ this.options.routes = (0, _javascript.mergeByKey)(this.options.routes, 'methodName', {
230
+ deep: true
231
+ });
232
+ }
233
+ constructor(controller, // if options is provided as a function, the consumer has to maintain the merge strategy
234
+ options){
235
+ this.controller = controller;
236
+ this.decorators = [];
237
+ this.options = typeof options === 'function' ? // we'll need to call it again after receiving the user options to adjust its routes
238
+ options(this.adjustRoutes(_defaultoptions.defaultOptions)) : (0, _mergeoptions.mergeOptions)(_defaultoptions.defaultOptions, options || {});
239
+ this.options = this.adjustRoutes({
240
+ ...this.options,
241
+ // todo: merge matched routes instead of overriding them
242
+ // i.e. routes with the same methodName, httpMethod, and path
243
+ routes: [
244
+ ...this.options.routes || [],
245
+ ...this.getExistingMethods()
246
+ ]
247
+ });
248
+ // if path is not provided, use the class name, without the suffix `Controller`,
249
+ // after converting it into url-friendly kebab-case
250
+ if (this.options.path === undefined) {
251
+ this.options.path = (0, _javascript.toKebabCase)(this.controller.name.replace(/Controller$/, ''));
252
+ }
253
+ this.createRoutes();
254
+ this.mergeRoutes();
255
+ (0, _addcontrollerpath.addControllerPath)(this);
256
+ (0, _addcontrollertags.addControllerTags)(this);
257
+ (0, _reflect.applyDecorators)(this);
258
+ }
259
+ };
260
+
261
+ //# sourceMappingURL=controller.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/decorators/controller/controller.factory.ts"],"sourcesContent":["import { Body, Param, Query, Req } from '@nestjs/common';\nimport type {\n ControllerOptions,\n HttpMethod,\n InitControllerOptions,\n InitRoute,\n IRoute,\n} from './types';\nimport { addHttpMethod } from './add-decorators/add-http-method';\nimport { addSwaggerParams } from './add-decorators/add-swagger-params';\nimport { routeHandler } from './route-handler';\nimport { addSwaggerOperation } from './add-decorators/add-swagger-operation';\nimport { addSwaggerBody } from './add-decorators/add-swagger-body';\nimport { addSwaggerResponse } from './add-decorators/add-swagger-response';\nimport { QueryInterceptor } from './interceptors/query.interceptor';\nimport { defaultOptions } from './default-options';\nimport { mergeOptions } from './utils/merge-options';\nimport { addControllerTags } from './add-decorators/add-controller-tags';\nimport { merge, mergeByKey, removeValues } from '@impactor/javascript';\nimport { applyDecorators } from './utils/reflect';\nimport { addControllerPath } from './add-decorators/add-controller-path';\nimport { toKebabCase } from '@impactor/javascript';\n// import 'reflect-metadata';\n\nexport class ControllerFactory {\n public options: ControllerOptions;\n public decorators: ClassDecorator[] = [];\n\n constructor(\n public controller: Function,\n // if options is provided as a function, the consumer has to maintain the merge strategy\n options?:\n | InitControllerOptions\n | ((defaultOptions: ControllerOptions) => ControllerOptions),\n ) {\n this.options =\n typeof options === 'function'\n ? // The consumer should receive the adjusted routes, so we need to call `adjustRoutes()` here\n // we'll need to call it again after receiving the user options to adjust its routes\n options(this.adjustRoutes(defaultOptions))\n : mergeOptions(defaultOptions, options || {});\n\n this.options = this.adjustRoutes({\n ...this.options,\n // todo: merge matched routes instead of overriding them\n // i.e. routes with the same methodName, httpMethod, and path\n routes: [...(this.options.routes || []), ...this.getExistingMethods()],\n });\n\n // if path is not provided, use the class name, without the suffix `Controller`,\n // after converting it into url-friendly kebab-case\n if (this.options.path === undefined) {\n this.options.path = toKebabCase(\n this.controller.name.replace(/Controller$/, ''),\n );\n }\n\n this.createRoutes();\n this.mergeRoutes();\n\n addControllerPath(this);\n addControllerTags(this);\n\n applyDecorators(this);\n }\n\n getExistingMethods() {\n let proto = this.controller.prototype;\n\n return (\n Reflect.ownKeys(proto)\n .filter((key) => typeof proto[key] === 'function')\n // convert symbol to string\n .map(String)\n .filter(\n (key) =>\n key !== 'constructor' &&\n // a method can be excluded from being handling by this factory, by adding `@Route(false)`\n Reflect.getMetadata('routeOptions', proto, key) !== false &&\n // skip methods that are already included in routes[]\n !this.options.routes?.some((route) => route.methodName === key),\n )\n // convert method to InitRoute{}\n .map((methodName) => {\n let routeOptions: InitRoute = Reflect.getMetadata(\n 'routeOptions',\n proto,\n methodName,\n );\n\n return {\n methodName,\n path: Reflect.getMetadata('path', proto[methodName]),\n ...routeOptions,\n } as InitRoute;\n })\n );\n }\n /**\n * add all missing props to each route, uch as the HTTP method, primaryKey, and operationId .\n * @param opts\n * @returns\n */\n adjustRoutes(opts: InitControllerOptions = this.options): ControllerOptions {\n return <ControllerOptions>{\n ...opts,\n routes: opts?.routes?.map((el) => {\n let route = { ...el };\n\n // NestJs request methods\n let requestMethods = [\n 'GET',\n 'POST',\n 'PUT',\n 'DELETE',\n 'PATCH',\n 'ALL',\n 'OPTIONS',\n 'HEAD',\n 'SEARCH',\n ];\n // capitalize route.httpMethod to much the HttpMethod type\n route.httpMethod = <HttpMethod>(\n (typeof route.httpMethod === 'number'\n ? requestMethods[route.httpMethod]\n : (<HttpMethod>route.httpMethod)?.toUpperCase() || 'GET')\n );\n route.primaryKey = route.primaryKey || opts.primaryKey || 'id';\n\n // generate the default methodName from path\n // examples\n // - {path: \"users/:id\"} -> \"getOneUserById\"\n // - {path: \"users/:userId/profiles/:profileId/posts\", many:true} -> \"getManyUsersByUserIdProfilesProfileIdPosts\"\n route.methodName =\n route.methodName ||\n route.httpMethod.toLowerCase() +\n (route.httpMethod === 'POST' ? '' : route.many ? 'Many' : 'One');\n\n route.operationId =\n route.operationId ||\n `${this.controller.name}_${String(route.methodName)}`;\n\n if (!route.path) {\n // for \"*One()\" operations, add /:id path param\n // example: GET /users/:id\n route.path =\n route.many || ['POST'].includes(route.httpMethod)\n ? '/'\n : `/:${route.primaryKey}`;\n }\n\n route.pathParams = route.pathParams\n ? route.pathParams.map((param) =>\n typeof param === 'string'\n ? { name: param, schema: { type: 'string' }, required: true }\n : param,\n )\n : // generate path params from route.path\n // matches `:id`, `/:id` and `other/:id/:userId`\n [...route.path.matchAll(/^\\/?:([^/]+)/g)].map((match) => ({\n name: match[1],\n schema: { type: 'string' },\n required: true,\n }));\n\n // todo: generate the corresponding service methods\n // +route.path\n // // remove the leading \"/\"\n // .replace(/^\\//, '')\n // .replace(/^:/,'By')\n // // replace \"/:id\" with \"ById\"\n // .replaceAll(/\\/:/g, '/By')\n // .split('/')\n // // convert parts to camel case\n // // example: \"users/profiles\" -> \"usersProfiles\"\n // .map((el, idx) =>\n // idx === 0 ? el : el.charAt(0).toUpperCase() + el.slice(1),\n // )\n // .join('');\n\n // override options per route\n // for example: route.primaryKey = route.primaryKey || opts.primaryKey\n // when opts.model is a class that extends another class, an exception is thrown `OtherClass is not defined`\n // todo: we may need to exclude other keys that have a class value such as errorResponseModel\n let { routes, model, ...otherOptions } = opts;\n // todo: use merge()?\n route = <IRoute>(\n merge(\n [otherOptions || {}, route || {}].map((el) =>\n removeValues(el, [undefined, null]),\n ),\n )\n );\n\n // merge arrays\n // todo: use mergeArray()?\n ['decorators', 'interceptors'].map((key) => {\n route[key as keyof typeof route] = [\n ...(opts[key as keyof typeof opts] || []),\n ...(route[key as keyof typeof route] || []),\n ];\n });\n\n // merge empty arrays\n // for example if route.interceptors=[] but opts.interceptors has values, opts.interceptors is used\n // Object.keys(opts).map((key) => {\n // if (Array.isArray(key) && !route[key as keyof typeof route]?.length) {\n // route[key as keyof typeof route] =\n // opts[key as keyof typeof opts] || [];\n // }\n // });\n\n route.interceptors!.push(\n new QueryInterceptor(\n merge([opts.query || {}, route.query || {}]) as {\n [key: string]: any;\n },\n ),\n );\n\n return route;\n }),\n };\n }\n\n /**\n * add options.routes[] as class methods\n */\n createRoutes() {\n let routes = this.options.routes?.filter(\n (route) => route.disabled === undefined || !route.disabled,\n ),\n controller = this.controller.prototype;\n\n for (let route of routes) {\n // create a route method and add it to the controller\n // todo: use interceptor to create ParsedRequest\n if (!controller[route.methodName]) {\n controller[route.methodName] = routeHandler(route, this);\n\n // inject the required params\n // similar to patchOne(@Req() req: Request, @Query() query, @Body() dto: MyEntity, @Param(\"id\") id:string)\n // we can get other info such as body and params from req\n // without the need to inject @Body() and @Query()\n // todo: route.primary || getPrimaryKeyFromEntity(opts.model) || \"id\"\n let paramKey = route.primaryKey || 'id';\n Req()(this.controller.prototype, route.methodName, 0);\n Query()(this.controller.prototype, route.methodName, 1);\n\n if (['GET', 'DELETE'].includes(route.httpMethod) && !route.many) {\n Param(paramKey)(this.controller.prototype, route.methodName, 2);\n } else if (route.httpMethod === 'POST') {\n Body()(this.controller.prototype, route.methodName, 2);\n // add 'design:paramtypes' to define the types of each param\n // for instance, the param `@Body()` should be defined as `this.options.model`,\n // so `ValidationPipe()` can work fine.\n // without it, wrong body will bypass class-validator and class-transformer decorators such as `@IsString()`\n Reflect.defineMetadata(\n 'design:paramtypes',\n // 0: `@Req() req: Request`\n // 1: `@Query() query: any` -> todo: add type definition\n // 2: `@Body() body: Model`\n [Request, Object, this.options.model],\n this.controller.prototype,\n route.methodName,\n );\n } else if (['PATCH', 'PUT'].includes(route.httpMethod)) {\n Body()(this.controller.prototype, route.methodName, 2);\n if (route.many) {\n Reflect.defineMetadata(\n 'design:paramtypes',\n [Object, Object, this.options.model],\n this.controller.prototype,\n route.methodName,\n );\n } else {\n // todo: or use `Param()` without `paramKey`, then use it like param[paramKey]\n // todo: accept list of pipes for each param -> `Param(paramName, ...pipes)(...)`\n // to accept only string or number -> add a validation in routerHandler i.e. `if(! typeof params[paramKey]) throw ...`,\n // as class-validator will not validate its type because it is not a DTO i.e. `getOne(@param() params: ParamsDTO)`\n Param(paramKey)(this.controller.prototype, route.methodName, 3);\n Reflect.defineMetadata(\n 'design:paramtypes',\n [Object, Object, this.options.model, Object],\n this.controller.prototype,\n route.methodName,\n );\n }\n }\n }\n\n // add decorators to the route\n // annotations are added only if:\n // - the method listed in this.options.routes[] and doesn't has @Route(false)\n // - the method is not listed and has @Route(options?)\n // if the method is not listed and doesn't has @Route(), the annotations will not be added\n // if no http decorator already applied such as @GET()\n // when adding @Get(), nest adds \"method\" and \"path\" metadata\n addHttpMethod(route, this);\n addSwaggerOperation(route, this);\n addSwaggerParams(route, this);\n addSwaggerBody(route, this);\n addSwaggerResponse(route, this);\n }\n }\n\n /**\n * Merges routes that share the same methodName.\n */\n mergeRoutes() {\n this.options.routes = mergeByKey<IRoute>(\n this.options.routes,\n 'methodName',\n {\n deep: true,\n },\n );\n }\n}\n"],"names":["ControllerFactory","getExistingMethods","proto","controller","prototype","Reflect","ownKeys","filter","key","map","String","getMetadata","options","routes","some","route","methodName","routeOptions","path","adjustRoutes","opts","el","requestMethods","httpMethod","toUpperCase","primaryKey","toLowerCase","many","operationId","name","includes","pathParams","param","schema","type","required","matchAll","match","model","otherOptions","merge","removeValues","undefined","interceptors","push","QueryInterceptor","query","createRoutes","disabled","routeHandler","paramKey","Req","Query","Param","Body","defineMetadata","Request","Object","addHttpMethod","addSwaggerOperation","addSwaggerParams","addSwaggerBody","addSwaggerResponse","mergeRoutes","mergeByKey","deep","decorators","defaultOptions","mergeOptions","toKebabCase","replace","addControllerPath","addControllerTags","applyDecorators"],"mappings":";;;;+BAwBaA;;;eAAAA;;;wBAxB2B;+BAQV;kCACG;8BACJ;qCACO;gCACL;oCACI;kCACF;gCACF;8BACF;mCACK;4BACc;yBAChB;mCACE;AAI3B,IAAA,AAAMA,oBAAN,MAAMA;IA0CXC,qBAAqB;QACnB,IAAIC,QAAQ,IAAI,CAACC,UAAU,CAACC,SAAS;QAErC,OACEC,QAAQC,OAAO,CAACJ,OACbK,MAAM,CAAC,CAACC,MAAQ,OAAON,KAAK,CAACM,IAAI,KAAK,WACvC,2BAA2B;SAC1BC,GAAG,CAACC,QACJH,MAAM,CACL,CAACC,MACCA,QAAQ,iBACR,0FAA0F;YAC1FH,QAAQM,WAAW,CAAC,gBAAgBT,OAAOM,SAAS,SACpD,qDAAqD;YACrD,CAAC,IAAI,CAACI,OAAO,CAACC,MAAM,EAAEC,KAAK,CAACC,QAAUA,MAAMC,UAAU,KAAKR,KAE/D,gCAAgC;SAC/BC,GAAG,CAAC,CAACO;YACJ,IAAIC,eAA0BZ,QAAQM,WAAW,CAC/C,gBACAT,OACAc;YAGF,OAAO;gBACLA;gBACAE,MAAMb,QAAQM,WAAW,CAAC,QAAQT,KAAK,CAACc,WAAW;gBACnD,GAAGC,YAAY;YACjB;QACF;IAEN;IACA;;;;GAIC,GACDE,aAAaC,OAA8B,IAAI,CAACR,OAAO,EAAqB;QAC1E,OAA0B;YACxB,GAAGQ,IAAI;YACPP,QAAQO,MAAMP,QAAQJ,IAAI,CAACY;gBACzB,IAAIN,QAAQ;oBAAE,GAAGM,EAAE;gBAAC;gBAEpB,yBAAyB;gBACzB,IAAIC,iBAAiB;oBACnB;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;oBACA;iBACD;gBACD,0DAA0D;gBAC1DP,MAAMQ,UAAU,GACb,OAAOR,MAAMQ,UAAU,KAAK,WACzBD,cAAc,CAACP,MAAMQ,UAAU,CAAC,GAChC,AAAaR,MAAMQ,UAAU,EAAGC,iBAAiB;gBAEvDT,MAAMU,UAAU,GAAGV,MAAMU,UAAU,IAAIL,KAAKK,UAAU,IAAI;gBAE1D,4CAA4C;gBAC5C,WAAW;gBACX,8CAA8C;gBAC9C,mHAAmH;gBACnHV,MAAMC,UAAU,GACdD,MAAMC,UAAU,IAChBD,MAAMQ,UAAU,CAACG,WAAW,KACzBX,CAAAA,MAAMQ,UAAU,KAAK,SAAS,KAAKR,MAAMY,IAAI,GAAG,SAAS,KAAI;gBAElEZ,MAAMa,WAAW,GACfb,MAAMa,WAAW,IACjB,GAAG,IAAI,CAACzB,UAAU,CAAC0B,IAAI,CAAC,CAAC,EAAEnB,OAAOK,MAAMC,UAAU,GAAG;gBAEvD,IAAI,CAACD,MAAMG,IAAI,EAAE;oBACf,+CAA+C;oBAC/C,0BAA0B;oBAC1BH,MAAMG,IAAI,GACRH,MAAMY,IAAI,IAAI;wBAAC;qBAAO,CAACG,QAAQ,CAACf,MAAMQ,UAAU,IAC5C,MACA,CAAC,EAAE,EAAER,MAAMU,UAAU,EAAE;gBAC/B;gBAEAV,MAAMgB,UAAU,GAAGhB,MAAMgB,UAAU,GAC/BhB,MAAMgB,UAAU,CAACtB,GAAG,CAAC,CAACuB,QACpB,OAAOA,UAAU,WACb;wBAAEH,MAAMG;wBAAOC,QAAQ;4BAAEC,MAAM;wBAAS;wBAAGC,UAAU;oBAAK,IAC1DH,SAGN,gDAAgD;gBAChD;uBAAIjB,MAAMG,IAAI,CAACkB,QAAQ,CAAC;iBAAiB,CAAC3B,GAAG,CAAC,CAAC4B,QAAW,CAAA;wBACxDR,MAAMQ,KAAK,CAAC,EAAE;wBACdJ,QAAQ;4BAAEC,MAAM;wBAAS;wBACzBC,UAAU;oBACZ,CAAA;gBAEJ,mDAAmD;gBACnD,eAAe;gBACf,gCAAgC;gBAChC,0BAA0B;gBAC1B,0BAA0B;gBAC1B,oCAAoC;gBACpC,iCAAiC;gBACjC,kBAAkB;gBAClB,qCAAqC;gBACrC,sDAAsD;gBACtD,wBAAwB;gBACxB,mEAAmE;gBACnE,QAAQ;gBACR,iBAAiB;gBAEjB,6BAA6B;gBAC7B,sEAAsE;gBACtE,4GAA4G;gBAC5G,6FAA6F;gBAC7F,IAAI,EAAEtB,MAAM,EAAEyB,KAAK,EAAE,GAAGC,cAAc,GAAGnB;gBACzC,qBAAqB;gBACrBL,QACEyB,IAAAA,iBAAK,EACH;oBAACD,gBAAgB,CAAC;oBAAGxB,SAAS,CAAC;iBAAE,CAACN,GAAG,CAAC,CAACY,KACrCoB,IAAAA,wBAAY,EAACpB,IAAI;wBAACqB;wBAAW;qBAAK;gBAKxC,eAAe;gBACf,0BAA0B;gBAC1B;oBAAC;oBAAc;iBAAe,CAACjC,GAAG,CAAC,CAACD;oBAClCO,KAAK,CAACP,IAA0B,GAAG;2BAC7BY,IAAI,CAACZ,IAAyB,IAAI,EAAE;2BACpCO,KAAK,CAACP,IAA0B,IAAI,EAAE;qBAC3C;gBACH;gBAEA,qBAAqB;gBACrB,mGAAmG;gBACnG,mCAAmC;gBACnC,2EAA2E;gBAC3E,yCAAyC;gBACzC,8CAA8C;gBAC9C,MAAM;gBACN,MAAM;gBAENO,MAAM4B,YAAY,CAAEC,IAAI,CACtB,IAAIC,kCAAgB,CAClBL,IAAAA,iBAAK,EAAC;oBAACpB,KAAK0B,KAAK,IAAI,CAAC;oBAAG/B,MAAM+B,KAAK,IAAI,CAAC;iBAAE;gBAM/C,OAAO/B;YACT;QACF;IACF;IAEA;;GAEC,GACDgC,eAAe;QACb,IAAIlC,SAAS,IAAI,CAACD,OAAO,CAACC,MAAM,EAAEN,OAC9B,CAACQ,QAAUA,MAAMiC,QAAQ,KAAKN,aAAa,CAAC3B,MAAMiC,QAAQ,GAE5D7C,aAAa,IAAI,CAACA,UAAU,CAACC,SAAS;QAExC,KAAK,IAAIW,SAASF,OAAQ;YACxB,qDAAqD;YACrD,gDAAgD;YAChD,IAAI,CAACV,UAAU,CAACY,MAAMC,UAAU,CAAC,EAAE;gBACjCb,UAAU,CAACY,MAAMC,UAAU,CAAC,GAAGiC,IAAAA,0BAAY,EAAClC,OAAO,IAAI;gBAEvD,6BAA6B;gBAC7B,0GAA0G;gBAC1G,yDAAyD;gBACzD,kDAAkD;gBAClD,qEAAqE;gBACrE,IAAImC,WAAWnC,MAAMU,UAAU,IAAI;gBACnC0B,IAAAA,WAAG,IAAG,IAAI,CAAChD,UAAU,CAACC,SAAS,EAAEW,MAAMC,UAAU,EAAE;gBACnDoC,IAAAA,aAAK,IAAG,IAAI,CAACjD,UAAU,CAACC,SAAS,EAAEW,MAAMC,UAAU,EAAE;gBAErD,IAAI;oBAAC;oBAAO;iBAAS,CAACc,QAAQ,CAACf,MAAMQ,UAAU,KAAK,CAACR,MAAMY,IAAI,EAAE;oBAC/D0B,IAAAA,aAAK,EAACH,UAAU,IAAI,CAAC/C,UAAU,CAACC,SAAS,EAAEW,MAAMC,UAAU,EAAE;gBAC/D,OAAO,IAAID,MAAMQ,UAAU,KAAK,QAAQ;oBACtC+B,IAAAA,YAAI,IAAG,IAAI,CAACnD,UAAU,CAACC,SAAS,EAAEW,MAAMC,UAAU,EAAE;oBACpD,4DAA4D;oBAC5D,+EAA+E;oBAC/E,uCAAuC;oBACvC,4GAA4G;oBAC5GX,QAAQkD,cAAc,CACpB,qBACA,2BAA2B;oBAC3B,wDAAwD;oBACxD,2BAA2B;oBAC3B;wBAACC;wBAASC;wBAAQ,IAAI,CAAC7C,OAAO,CAAC0B,KAAK;qBAAC,EACrC,IAAI,CAACnC,UAAU,CAACC,SAAS,EACzBW,MAAMC,UAAU;gBAEpB,OAAO,IAAI;oBAAC;oBAAS;iBAAM,CAACc,QAAQ,CAACf,MAAMQ,UAAU,GAAG;oBACtD+B,IAAAA,YAAI,IAAG,IAAI,CAACnD,UAAU,CAACC,SAAS,EAAEW,MAAMC,UAAU,EAAE;oBACpD,IAAID,MAAMY,IAAI,EAAE;wBACdtB,QAAQkD,cAAc,CACpB,qBACA;4BAACE;4BAAQA;4BAAQ,IAAI,CAAC7C,OAAO,CAAC0B,KAAK;yBAAC,EACpC,IAAI,CAACnC,UAAU,CAACC,SAAS,EACzBW,MAAMC,UAAU;oBAEpB,OAAO;wBACL,8EAA8E;wBAC9E,iFAAiF;wBACjF,uHAAuH;wBACvH,kHAAkH;wBAClHqC,IAAAA,aAAK,EAACH,UAAU,IAAI,CAAC/C,UAAU,CAACC,SAAS,EAAEW,MAAMC,UAAU,EAAE;wBAC7DX,QAAQkD,cAAc,CACpB,qBACA;4BAACE;4BAAQA;4BAAQ,IAAI,CAAC7C,OAAO,CAAC0B,KAAK;4BAAEmB;yBAAO,EAC5C,IAAI,CAACtD,UAAU,CAACC,SAAS,EACzBW,MAAMC,UAAU;oBAEpB;gBACF;YACF;YAEA,8BAA8B;YAC9B,iCAAiC;YACjC,6EAA6E;YAC7E,sDAAsD;YACtD,0FAA0F;YAC1F,sDAAsD;YACtD,6DAA6D;YAC7D0C,IAAAA,4BAAa,EAAC3C,OAAO,IAAI;YACzB4C,IAAAA,wCAAmB,EAAC5C,OAAO,IAAI;YAC/B6C,IAAAA,kCAAgB,EAAC7C,OAAO,IAAI;YAC5B8C,IAAAA,8BAAc,EAAC9C,OAAO,IAAI;YAC1B+C,IAAAA,sCAAkB,EAAC/C,OAAO,IAAI;QAChC;IACF;IAEA;;GAEC,GACDgD,cAAc;QACZ,IAAI,CAACnD,OAAO,CAACC,MAAM,GAAGmD,IAAAA,sBAAU,EAC9B,IAAI,CAACpD,OAAO,CAACC,MAAM,EACnB,cACA;YACEoD,MAAM;QACR;IAEJ;IAjSA,YACE,AAAO9D,UAAoB,EAC3B,yFAAyF;IACzFS,OAE8D,CAC9D;aALOT,aAAAA;aAHF+D,aAA+B,EAAE;QAStC,IAAI,CAACtD,OAAO,GACV,OAAOA,YAAY,aAEf,oFAAoF;QACpFA,QAAQ,IAAI,CAACO,YAAY,CAACgD,8BAAc,KACxCC,IAAAA,0BAAY,EAACD,8BAAc,EAAEvD,WAAW,CAAC;QAE/C,IAAI,CAACA,OAAO,GAAG,IAAI,CAACO,YAAY,CAAC;YAC/B,GAAG,IAAI,CAACP,OAAO;YACf,wDAAwD;YACxD,6DAA6D;YAC7DC,QAAQ;mBAAK,IAAI,CAACD,OAAO,CAACC,MAAM,IAAI,EAAE;mBAAM,IAAI,CAACZ,kBAAkB;aAAG;QACxE;QAEA,gFAAgF;QAChF,mDAAmD;QACnD,IAAI,IAAI,CAACW,OAAO,CAACM,IAAI,KAAKwB,WAAW;YACnC,IAAI,CAAC9B,OAAO,CAACM,IAAI,GAAGmD,IAAAA,uBAAW,EAC7B,IAAI,CAAClE,UAAU,CAAC0B,IAAI,CAACyC,OAAO,CAAC,eAAe;QAEhD;QAEA,IAAI,CAACvB,YAAY;QACjB,IAAI,CAACgB,WAAW;QAEhBQ,IAAAA,oCAAiB,EAAC,IAAI;QACtBC,IAAAA,oCAAiB,EAAC,IAAI;QAEtBC,IAAAA,wBAAe,EAAC,IAAI;IACtB;AA8PF"}
@@ -0,0 +1,2 @@
1
+ import type { InitControllerOptions } from './types';
2
+ export declare const defaultOptions: Partial<InitControllerOptions>;
@@ -0,0 +1,181 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "defaultOptions", {
7
+ enumerable: true,
8
+ get: function() {
9
+ return defaultOptions;
10
+ }
11
+ });
12
+ const defaultOptions = {
13
+ // todo: allow sub routes, such as `/:field`
14
+ routes: [
15
+ {
16
+ httpMethod: 'GET',
17
+ // todo: `retrieve a single ${entity.name || "record"}`
18
+ summary: 'retrieve a single record'
19
+ },
20
+ {
21
+ httpMethod: 'GET',
22
+ many: true,
23
+ // todo: change to "retrieve multiple $entity records"
24
+ summary: 'retrieve multiple records',
25
+ description: 'see <a href="https://typeorm.io/docs/working-with-entity-manager/find-options" target="_blank">TypeOrm find options</a>'
26
+ },
27
+ {
28
+ httpMethod: 'POST',
29
+ summary: 'create a single or multiple records'
30
+ },
31
+ {
32
+ httpMethod: 'PATCH',
33
+ summary: 'update a single record by primary key'
34
+ },
35
+ {
36
+ httpMethod: 'PATCH',
37
+ summary: 'update multiple records',
38
+ many: true
39
+ },
40
+ {
41
+ httpMethod: 'PUT',
42
+ summary: 'replace a single record'
43
+ },
44
+ {
45
+ httpMethod: 'PUT',
46
+ summary: 'replace multiple records',
47
+ many: true
48
+ },
49
+ {
50
+ httpMethod: 'DELETE',
51
+ summary: 'delete a record by primary key'
52
+ },
53
+ {
54
+ httpMethod: 'DELETE',
55
+ summary: 'delete multiple records',
56
+ many: true
57
+ }
58
+ ],
59
+ // query params should be similar to typeOrm methods, such as findOne({relations, withDeleted, ...})
60
+ // so query params should be ?relations&withDeleted
61
+ // https://orkhan.gitbook.io/typeorm/docs/find-options
62
+ queryParams: [
63
+ {
64
+ name: 'take',
65
+ description: 'limit resource count.',
66
+ example: 50,
67
+ many: true,
68
+ httpMethods: [
69
+ 'GET',
70
+ 'DELETE',
71
+ 'PATCH',
72
+ 'PUT'
73
+ ]
74
+ },
75
+ {
76
+ name: 'skip',
77
+ description: 'skip rows.',
78
+ example: 2,
79
+ many: true,
80
+ httpMethods: [
81
+ 'GET',
82
+ 'DELETE',
83
+ 'PATCH',
84
+ 'PUT'
85
+ ]
86
+ },
87
+ {
88
+ name: 'select',
89
+ description: `Selects resource fields.`,
90
+ example: [
91
+ 'name',
92
+ 'email'
93
+ ],
94
+ required: false,
95
+ httpMethods: [
96
+ 'GET'
97
+ ]
98
+ },
99
+ {
100
+ name: 'relations',
101
+ description: 'add joined relational objects, supports nested relations',
102
+ example: [
103
+ 'user.profile:name,age'
104
+ ],
105
+ httpMethods: [
106
+ 'GET'
107
+ ]
108
+ },
109
+ {
110
+ name: 'where',
111
+ description: 'add where condition. to use sql statement, start the value with `>`.\n to search in an array column, put the value in an array. see <a href="https://orkhan.gitbook.io/typeorm/docs/find-options#advanced-options" target="_blank">see advanced options</a>',
112
+ example: {
113
+ name: 'john',
114
+ email: ">Like '%john%'"
115
+ },
116
+ many: true,
117
+ httpMethods: [
118
+ 'GET',
119
+ 'PATCH',
120
+ 'PUT',
121
+ 'DELETE'
122
+ ]
123
+ },
124
+ {
125
+ name: 'order',
126
+ // or 'name,id:DESC'
127
+ example: {
128
+ name: 'ASC',
129
+ id: 'DESC'
130
+ },
131
+ many: true,
132
+ httpMethods: [
133
+ 'GET',
134
+ 'PATCH',
135
+ 'PUT',
136
+ 'DELETE'
137
+ ]
138
+ },
139
+ {
140
+ name: 'cache',
141
+ description: 'Enables or disables query result caching',
142
+ type: 'boolean',
143
+ httpMethods: [
144
+ 'GET'
145
+ ]
146
+ },
147
+ {
148
+ name: 'withDeleted',
149
+ // todo:"default:${route.many ? 'false' : 'true'}"
150
+ description: `include soft deleted records`,
151
+ type: 'boolean',
152
+ httpMethods: [
153
+ 'GET'
154
+ ]
155
+ },
156
+ {
157
+ name: 'softDelete',
158
+ description: 'whether to perform a soft or hard delete, the default is true',
159
+ type: 'boolean',
160
+ httpMethods: [
161
+ 'DELETE'
162
+ ]
163
+ },
164
+ {
165
+ name: 'lock',
166
+ description: 'Enables locking mechanism for query',
167
+ httpMethods: [
168
+ 'GET'
169
+ ],
170
+ many: false,
171
+ example: {
172
+ mode: 'optimistic',
173
+ version: 1
174
+ }
175
+ }
176
+ ],
177
+ serviceName: 'service',
178
+ primaryKey: 'id'
179
+ };
180
+
181
+ //# sourceMappingURL=default-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/decorators/controller/default-options.ts"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/no-unused-vars\nimport { UpdateResponseDto } from './dto/update-response.dto';\nimport type { InitControllerOptions } from './types';\n// todo: add opts.maxTake to limit ?take=\n\n/**\n * the default options for \\@Controller()\n */\nexport const defaultOptions: Partial<InitControllerOptions> = {\n // todo: allow sub routes, such as `/:field`\n routes: [\n {\n httpMethod: 'GET',\n // todo: `retrieve a single ${entity.name || \"record\"}`\n summary: 'retrieve a single record',\n },\n {\n httpMethod: 'GET',\n many: true,\n // todo: change to \"retrieve multiple $entity records\"\n summary: 'retrieve multiple records',\n description:\n 'see <a href=\"https://typeorm.io/docs/working-with-entity-manager/find-options\" target=\"_blank\">TypeOrm find options</a>',\n },\n {\n httpMethod: 'POST',\n summary: 'create a single or multiple records',\n },\n {\n httpMethod: 'PATCH',\n summary: 'update a single record by primary key',\n // todo: this causes an error in swagger UI\n // `swagger-ui-init.js:1597 Uncaught ReferenceError: __name is not defined`\n // https://chat.deepseek.com/a/chat/s/e76e6f76-1a5e-4c49-a63f-6d44ce619906\n // responseModel: UpdateResponseDto,\n },\n {\n httpMethod: 'PATCH',\n summary: 'update multiple records',\n many: true,\n // responseModel: UpdateResponseDto,\n },\n {\n httpMethod: 'PUT',\n summary: 'replace a single record',\n // responseModel: UpdateResponseDto,\n },\n {\n httpMethod: 'PUT',\n summary: 'replace multiple records',\n many: true,\n // responseModel: UpdateResponseDto,\n },\n {\n httpMethod: 'DELETE',\n summary: 'delete a record by primary key',\n // responseModel: UpdateResponseDto,\n },\n {\n httpMethod: 'DELETE',\n summary: 'delete multiple records',\n many: true,\n // responseModel: UpdateResponseDto,\n },\n // todo: add update multiple records\n ],\n // query params should be similar to typeOrm methods, such as findOne({relations, withDeleted, ...})\n // so query params should be ?relations&withDeleted\n // https://orkhan.gitbook.io/typeorm/docs/find-options\n queryParams: [\n {\n name: 'take',\n description: 'limit resource count.',\n example: 50,\n many: true,\n httpMethods: ['GET', 'DELETE', 'PATCH', 'PUT'],\n },\n {\n name: 'skip',\n description: 'skip rows.',\n example: 2,\n many: true,\n httpMethods: ['GET', 'DELETE', 'PATCH', 'PUT'],\n },\n {\n name: 'select',\n description: `Selects resource fields.`,\n example: ['name', 'email'],\n required: false,\n httpMethods: ['GET'],\n },\n {\n name: 'relations',\n description: 'add joined relational objects, supports nested relations',\n example: ['user.profile:name,age'],\n httpMethods: ['GET'],\n },\n {\n name: 'where',\n description:\n 'add where condition. to use sql statement, start the value with `>`.\\n to search in an array column, put the value in an array. see <a href=\"https://orkhan.gitbook.io/typeorm/docs/find-options#advanced-options\" target=\"_blank\">see advanced options</a>',\n example: { name: 'john', email: \">Like '%john%'\" },\n many: true,\n httpMethods: ['GET', 'PATCH', 'PUT', 'DELETE'],\n },\n {\n name: 'order',\n // or 'name,id:DESC'\n example: { name: 'ASC', id: 'DESC' },\n many: true,\n httpMethods: ['GET', 'PATCH', 'PUT', 'DELETE'],\n },\n {\n name: 'cache',\n description: 'Enables or disables query result caching',\n type: 'boolean',\n httpMethods: ['GET'],\n },\n {\n name: 'withDeleted',\n // todo:\"default:${route.many ? 'false' : 'true'}\"\n description: `include soft deleted records`,\n type: 'boolean',\n httpMethods: ['GET'],\n },\n {\n name: 'softDelete',\n description:\n 'whether to perform a soft or hard delete, the default is true',\n type: 'boolean',\n httpMethods: ['DELETE'],\n },\n {\n name: 'lock',\n description: 'Enables locking mechanism for query',\n httpMethods: ['GET'],\n many: false,\n example: { mode: 'optimistic', version: 1 },\n },\n ],\n serviceName: 'service',\n primaryKey: 'id',\n};\n"],"names":["defaultOptions","routes","httpMethod","summary","many","description","queryParams","name","example","httpMethods","required","email","id","type","mode","version","serviceName","primaryKey"],"mappings":"AAAA,6DAA6D;;;;;+BAQhDA;;;eAAAA;;;AAAN,MAAMA,iBAAiD;IAC5D,4CAA4C;IAC5CC,QAAQ;QACN;YACEC,YAAY;YACZ,uDAAuD;YACvDC,SAAS;QACX;QACA;YACED,YAAY;YACZE,MAAM;YACN,sDAAsD;YACtDD,SAAS;YACTE,aACE;QACJ;QACA;YACEH,YAAY;YACZC,SAAS;QACX;QACA;YACED,YAAY;YACZC,SAAS;QAKX;QACA;YACED,YAAY;YACZC,SAAS;YACTC,MAAM;QAER;QACA;YACEF,YAAY;YACZC,SAAS;QAEX;QACA;YACED,YAAY;YACZC,SAAS;YACTC,MAAM;QAER;QACA;YACEF,YAAY;YACZC,SAAS;QAEX;QACA;YACED,YAAY;YACZC,SAAS;YACTC,MAAM;QAER;KAED;IACD,oGAAoG;IACpG,mDAAmD;IACnD,sDAAsD;IACtDE,aAAa;QACX;YACEC,MAAM;YACNF,aAAa;YACbG,SAAS;YACTJ,MAAM;YACNK,aAAa;gBAAC;gBAAO;gBAAU;gBAAS;aAAM;QAChD;QACA;YACEF,MAAM;YACNF,aAAa;YACbG,SAAS;YACTJ,MAAM;YACNK,aAAa;gBAAC;gBAAO;gBAAU;gBAAS;aAAM;QAChD;QACA;YACEF,MAAM;YACNF,aAAa,CAAC,wBAAwB,CAAC;YACvCG,SAAS;gBAAC;gBAAQ;aAAQ;YAC1BE,UAAU;YACVD,aAAa;gBAAC;aAAM;QACtB;QACA;YACEF,MAAM;YACNF,aAAa;YACbG,SAAS;gBAAC;aAAwB;YAClCC,aAAa;gBAAC;aAAM;QACtB;QACA;YACEF,MAAM;YACNF,aACE;YACFG,SAAS;gBAAED,MAAM;gBAAQI,OAAO;YAAiB;YACjDP,MAAM;YACNK,aAAa;gBAAC;gBAAO;gBAAS;gBAAO;aAAS;QAChD;QACA;YACEF,MAAM;YACN,oBAAoB;YACpBC,SAAS;gBAAED,MAAM;gBAAOK,IAAI;YAAO;YACnCR,MAAM;YACNK,aAAa;gBAAC;gBAAO;gBAAS;gBAAO;aAAS;QAChD;QACA;YACEF,MAAM;YACNF,aAAa;YACbQ,MAAM;YACNJ,aAAa;gBAAC;aAAM;QACtB;QACA;YACEF,MAAM;YACN,kDAAkD;YAClDF,aAAa,CAAC,4BAA4B,CAAC;YAC3CQ,MAAM;YACNJ,aAAa;gBAAC;aAAM;QACtB;QACA;YACEF,MAAM;YACNF,aACE;YACFQ,MAAM;YACNJ,aAAa;gBAAC;aAAS;QACzB;QACA;YACEF,MAAM;YACNF,aAAa;YACbI,aAAa;gBAAC;aAAM;YACpBL,MAAM;YACNI,SAAS;gBAAEM,MAAM;gBAAcC,SAAS;YAAE;QAC5C;KACD;IACDC,aAAa;IACbC,YAAY;AACd"}
@@ -0,0 +1,2 @@
1
+ export declare class EmptyDto {
2
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * an empty DTO
3
+ * can be used with routes that don't need to receive any data in its body
4
+ * or when the response is intended to be an empty object
5
+ */ "use strict";
6
+ Object.defineProperty(exports, "__esModule", {
7
+ value: true
8
+ });
9
+ Object.defineProperty(exports, "EmptyDto", {
10
+ enumerable: true,
11
+ get: function() {
12
+ return EmptyDto;
13
+ }
14
+ });
15
+ let EmptyDto = class EmptyDto {
16
+ };
17
+
18
+ //# sourceMappingURL=empty.dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/decorators/controller/dto/empty.dto.ts"],"sourcesContent":["/**\n * an empty DTO\n * can be used with routes that don't need to receive any data in its body\n * or when the response is intended to be an empty object\n */\nexport class EmptyDto {}\n"],"names":["EmptyDto"],"mappings":"AAAA;;;;CAIC;;;;+BACYA;;;eAAAA;;;AAAN,IAAA,AAAMA,WAAN,MAAMA;AAAU"}
@@ -0,0 +1,3 @@
1
+ export declare class UpdateResponseDto {
2
+ affected: number;
3
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * a Dto for the response of the update or delete query
3
+ */ "use strict";
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ Object.defineProperty(exports, "UpdateResponseDto", {
8
+ enumerable: true,
9
+ get: function() {
10
+ return UpdateResponseDto;
11
+ }
12
+ });
13
+ let UpdateResponseDto = class UpdateResponseDto {
14
+ };
15
+
16
+ //# sourceMappingURL=update-response.dto.js.map