@achinet/nestjs-async 0.0.1 → 0.1.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 (246) hide show
  1. package/.eslintrc.js +31 -0
  2. package/CHANGELOG.md +6 -0
  3. package/dist/decorators/asyncapi-operation-for-meta-key.decorator.js +1 -2
  4. package/dist/decorators/asyncapi-operation-for-meta-key.decorator.js.map +1 -1
  5. package/dist/decorators/asyncapi-operation.decorator.js +1 -2
  6. package/dist/decorators/asyncapi-operation.decorator.js.map +1 -1
  7. package/dist/decorators/asyncapi-pub.decorator.js +1 -2
  8. package/dist/decorators/asyncapi-pub.decorator.js.map +1 -1
  9. package/dist/decorators/asyncapi-service.decorator.js +1 -2
  10. package/dist/decorators/asyncapi-service.decorator.js.map +1 -1
  11. package/dist/decorators/asyncapi-sub.decorator.js +1 -2
  12. package/dist/decorators/asyncapi-sub.decorator.js.map +1 -1
  13. package/dist/explorers/asyncapi-operation.explorer.d.ts +8 -8
  14. package/dist/sample/app.module.d.ts +2 -0
  15. package/dist/sample/app.module.js +20 -0
  16. package/dist/sample/app.module.js.map +1 -0
  17. package/dist/sample/common.d.ts +3 -0
  18. package/dist/sample/common.js +40 -0
  19. package/dist/sample/common.js.map +1 -0
  20. package/dist/sample/constants.d.ts +12 -0
  21. package/dist/sample/constants.js +16 -0
  22. package/dist/sample/constants.js.map +1 -0
  23. package/dist/sample/felines/class/cat.class.d.ts +4 -0
  24. package/dist/sample/felines/class/cat.class.js +24 -0
  25. package/dist/sample/felines/class/cat.class.js.map +1 -0
  26. package/dist/sample/felines/class/feline.class.d.ts +19 -0
  27. package/dist/sample/felines/class/feline.class.js +20 -0
  28. package/dist/sample/felines/class/feline.class.js.map +1 -0
  29. package/dist/sample/felines/class/index.d.ts +5 -0
  30. package/dist/sample/felines/class/index.js +22 -0
  31. package/dist/sample/felines/class/index.js.map +1 -0
  32. package/dist/sample/felines/class/lion.class.d.ts +4 -0
  33. package/dist/sample/felines/class/lion.class.js +8 -0
  34. package/dist/sample/felines/class/lion.class.js.map +1 -0
  35. package/dist/sample/felines/class/message.class.d.ts +7 -0
  36. package/dist/sample/felines/class/message.class.js +28 -0
  37. package/dist/sample/felines/class/message.class.js.map +1 -0
  38. package/dist/sample/felines/class/tiger.class.d.ts +4 -0
  39. package/dist/sample/felines/class/tiger.class.js +8 -0
  40. package/dist/sample/felines/class/tiger.class.js.map +1 -0
  41. package/dist/sample/felines/dto/create-feline.dto.d.ts +4 -0
  42. package/dist/sample/felines/dto/create-feline.dto.js +28 -0
  43. package/dist/sample/felines/dto/create-feline.dto.js.map +1 -0
  44. package/dist/sample/felines/dto/index.d.ts +2 -0
  45. package/dist/sample/felines/dto/index.js +19 -0
  46. package/dist/sample/felines/dto/index.js.map +1 -0
  47. package/dist/sample/felines/dto/journaling-data.dto.d.ts +4 -0
  48. package/dist/sample/felines/dto/journaling-data.dto.js +8 -0
  49. package/dist/sample/felines/dto/journaling-data.dto.js.map +1 -0
  50. package/dist/sample/felines/felines.constants.d.ts +1 -0
  51. package/dist/sample/felines/felines.constants.js +5 -0
  52. package/dist/sample/felines/felines.constants.js.map +1 -0
  53. package/dist/sample/felines/felines.controller.d.ts +15 -0
  54. package/dist/sample/felines/felines.controller.js +101 -0
  55. package/dist/sample/felines/felines.controller.js.map +1 -0
  56. package/dist/sample/felines/felines.gateway.d.ts +16 -0
  57. package/dist/sample/felines/felines.gateway.js +90 -0
  58. package/dist/sample/felines/felines.gateway.js.map +1 -0
  59. package/dist/sample/felines/felines.module.d.ts +2 -0
  60. package/dist/sample/felines/felines.module.js +28 -0
  61. package/dist/sample/felines/felines.module.js.map +1 -0
  62. package/dist/sample/felines/felines.service.d.ts +8 -0
  63. package/dist/sample/felines/felines.service.js +33 -0
  64. package/dist/sample/felines/felines.service.js.map +1 -0
  65. package/dist/sample/felines/rto/feline.rto.d.ts +7 -0
  66. package/dist/sample/felines/rto/feline.rto.js +31 -0
  67. package/dist/sample/felines/rto/feline.rto.js.map +1 -0
  68. package/dist/sample/felines/rto/index.d.ts +1 -0
  69. package/dist/sample/felines/rto/index.js +18 -0
  70. package/dist/sample/felines/rto/index.js.map +1 -0
  71. package/dist/sample/index.d.ts +3 -0
  72. package/dist/sample/index.js +20 -0
  73. package/dist/sample/index.js.map +1 -0
  74. package/dist/sample/main.d.ts +1 -0
  75. package/dist/sample/main.js +30 -0
  76. package/dist/sample/main.js.map +1 -0
  77. package/dist/src/asyncapi-document.builder.d.ts +27 -0
  78. package/dist/src/asyncapi-document.builder.js +110 -0
  79. package/dist/src/asyncapi-document.builder.js.map +1 -0
  80. package/dist/src/asyncapi.constants.d.ts +6 -0
  81. package/dist/src/asyncapi.constants.js +11 -0
  82. package/dist/src/asyncapi.constants.js.map +1 -0
  83. package/dist/src/asyncapi.module.d.ts +8 -0
  84. package/dist/src/asyncapi.module.js +47 -0
  85. package/dist/src/asyncapi.module.js.map +1 -0
  86. package/dist/src/binding/asyncapi.amqp.interfaces.d.ts +38 -0
  87. package/dist/src/binding/asyncapi.amqp.interfaces.js +3 -0
  88. package/dist/src/binding/asyncapi.amqp.interfaces.js.map +1 -0
  89. package/dist/src/binding/asyncapi.kafka.interfaces.d.ts +21 -0
  90. package/dist/src/binding/asyncapi.kafka.interfaces.js +3 -0
  91. package/dist/src/binding/asyncapi.kafka.interfaces.js.map +1 -0
  92. package/dist/src/binding/index.d.ts +2 -0
  93. package/dist/src/binding/index.js +19 -0
  94. package/dist/src/binding/index.js.map +1 -0
  95. package/dist/src/decorators/asyncapi-operation-for-meta-key.decorator.d.ts +2 -0
  96. package/dist/src/decorators/asyncapi-operation-for-meta-key.decorator.js +37 -0
  97. package/dist/src/decorators/asyncapi-operation-for-meta-key.decorator.js.map +1 -0
  98. package/dist/src/decorators/asyncapi-operation.decorator.d.ts +2 -0
  99. package/dist/src/decorators/asyncapi-operation.decorator.js +9 -0
  100. package/dist/src/decorators/asyncapi-operation.decorator.js.map +1 -0
  101. package/dist/src/decorators/asyncapi-pub.decorator.d.ts +2 -0
  102. package/dist/src/decorators/asyncapi-pub.decorator.js +10 -0
  103. package/dist/src/decorators/asyncapi-pub.decorator.js.map +1 -0
  104. package/dist/src/decorators/asyncapi-service.decorator.d.ts +1 -0
  105. package/dist/src/decorators/asyncapi-service.decorator.js +9 -0
  106. package/dist/src/decorators/asyncapi-service.decorator.js.map +1 -0
  107. package/dist/src/decorators/asyncapi-sub.decorator.d.ts +2 -0
  108. package/dist/src/decorators/asyncapi-sub.decorator.js +10 -0
  109. package/dist/src/decorators/asyncapi-sub.decorator.js.map +1 -0
  110. package/dist/src/decorators/index.d.ts +4 -0
  111. package/dist/src/decorators/index.js +21 -0
  112. package/dist/src/decorators/index.js.map +1 -0
  113. package/dist/src/explorers/asyncapi-class.explorer.d.ts +5 -0
  114. package/dist/src/explorers/asyncapi-class.explorer.js +27 -0
  115. package/dist/src/explorers/asyncapi-class.explorer.js.map +1 -0
  116. package/dist/src/explorers/asyncapi-operation.explorer.d.ts +17 -0
  117. package/dist/src/explorers/asyncapi-operation.explorer.js +26 -0
  118. package/dist/src/explorers/asyncapi-operation.explorer.js.map +1 -0
  119. package/dist/src/explorers/index.d.ts +2 -0
  120. package/dist/src/explorers/index.js +19 -0
  121. package/dist/src/explorers/index.js.map +1 -0
  122. package/dist/src/index.d.ts +4 -0
  123. package/dist/src/index.js +21 -0
  124. package/dist/src/index.js.map +1 -0
  125. package/dist/src/interface/asyncapi-common.interfaces.d.ts +126 -0
  126. package/dist/src/interface/asyncapi-common.interfaces.js +3 -0
  127. package/dist/src/interface/asyncapi-common.interfaces.js.map +1 -0
  128. package/dist/src/interface/asyncapi-document-options.interface.d.ts +3 -0
  129. package/dist/src/interface/asyncapi-document-options.interface.js +3 -0
  130. package/dist/src/interface/asyncapi-document-options.interface.js.map +1 -0
  131. package/dist/src/interface/asyncapi-message.interface.d.ts +8 -0
  132. package/dist/src/interface/asyncapi-message.interface.js +3 -0
  133. package/dist/src/interface/asyncapi-message.interface.js.map +1 -0
  134. package/dist/src/interface/asyncapi-operation-headers.interface.d.ts +6 -0
  135. package/dist/src/interface/asyncapi-operation-headers.interface.js +3 -0
  136. package/dist/src/interface/asyncapi-operation-headers.interface.js.map +1 -0
  137. package/dist/src/interface/asyncapi-operation-options-raw.interface.d.ts +21 -0
  138. package/dist/src/interface/asyncapi-operation-options-raw.interface.js +3 -0
  139. package/dist/src/interface/asyncapi-operation-options-raw.interface.js.map +1 -0
  140. package/dist/src/interface/asyncapi-operation-options.interface.d.ts +8 -0
  141. package/dist/src/interface/asyncapi-operation-options.interface.js +3 -0
  142. package/dist/src/interface/asyncapi-operation-options.interface.js.map +1 -0
  143. package/dist/src/interface/asyncapi-operation-payload.interface.d.ts +2 -0
  144. package/dist/src/interface/asyncapi-operation-payload.interface.js +3 -0
  145. package/dist/src/interface/asyncapi-operation-payload.interface.js.map +1 -0
  146. package/dist/src/interface/asyncapi-server.interface.d.ts +10 -0
  147. package/dist/src/interface/asyncapi-server.interface.js +3 -0
  148. package/dist/src/interface/asyncapi-server.interface.js.map +1 -0
  149. package/dist/src/interface/asyncapi-template-options.interface.d.ts +7 -0
  150. package/dist/src/interface/asyncapi-template-options.interface.js +3 -0
  151. package/dist/src/interface/asyncapi-template-options.interface.js.map +1 -0
  152. package/dist/src/interface/denormalized-doc-resolvers.interface.d.ts +6 -0
  153. package/dist/src/interface/denormalized-doc-resolvers.interface.js +3 -0
  154. package/dist/src/interface/denormalized-doc-resolvers.interface.js.map +1 -0
  155. package/dist/src/interface/denormalized-doc.interface.d.ts +10 -0
  156. package/dist/src/interface/denormalized-doc.interface.js +3 -0
  157. package/dist/src/interface/denormalized-doc.interface.js.map +1 -0
  158. package/dist/src/interface/generator-options.interface.d.ts +22 -0
  159. package/dist/src/interface/generator-options.interface.js +3 -0
  160. package/dist/src/interface/generator-options.interface.js.map +1 -0
  161. package/dist/src/interface/index.d.ts +11 -0
  162. package/dist/src/interface/index.js +28 -0
  163. package/dist/src/interface/index.js.map +1 -0
  164. package/dist/src/services/asyncapi.explorer.d.ts +12 -0
  165. package/dist/src/services/asyncapi.explorer.js +69 -0
  166. package/dist/src/services/asyncapi.explorer.js.map +1 -0
  167. package/dist/src/services/asyncapi.generator.d.ts +6 -0
  168. package/dist/src/services/asyncapi.generator.js +68 -0
  169. package/dist/src/services/asyncapi.generator.js.map +1 -0
  170. package/dist/src/services/asyncapi.scanner.d.ts +14 -0
  171. package/dist/src/services/asyncapi.scanner.js +71 -0
  172. package/dist/src/services/asyncapi.scanner.js.map +1 -0
  173. package/dist/src/services/asyncapi.transformer.d.ts +4 -0
  174. package/dist/src/services/asyncapi.transformer.js +33 -0
  175. package/dist/src/services/asyncapi.transformer.js.map +1 -0
  176. package/dist/src/services/index.d.ts +4 -0
  177. package/dist/src/services/index.js +21 -0
  178. package/dist/src/services/index.js.map +1 -0
  179. package/dist/src/services/operation-object.factory.d.ts +9 -0
  180. package/dist/src/services/operation-object.factory.js +37 -0
  181. package/dist/src/services/operation-object.factory.js.map +1 -0
  182. package/dist/test/configs/jest-base.config.d.ts +3 -0
  183. package/dist/test/configs/jest-base.config.js +20 -0
  184. package/dist/test/configs/jest-base.config.js.map +1 -0
  185. package/dist/test/configs/jest-e2e.config.d.ts +3 -0
  186. package/dist/test/configs/jest-e2e.config.js +16 -0
  187. package/dist/test/configs/jest-e2e.config.js.map +1 -0
  188. package/dist/test/express.e2e-spec.d.ts +1 -0
  189. package/dist/test/express.e2e-spec.js +89 -0
  190. package/dist/test/express.e2e-spec.js.map +1 -0
  191. package/dist/test/fastify.e2e-spec.d.ts +1 -0
  192. package/dist/test/fastify.e2e-spec.js +57 -0
  193. package/dist/test/fastify.e2e-spec.js.map +1 -0
  194. package/dist/tsconfig.tsbuildinfo +1 -1
  195. package/misc/references/ref.html +303 -0
  196. package/misc/references/ref.json +1 -0
  197. package/misc/references/ref.yaml +292 -0
  198. package/misc/take-snaphots.sh +8 -0
  199. package/package.json +18 -11
  200. package/sample/app.module.ts +7 -0
  201. package/sample/common.ts +49 -0
  202. package/sample/constants.ts +12 -0
  203. package/sample/felines/class/cat.class.ts +9 -0
  204. package/sample/felines/class/feline.class.ts +29 -0
  205. package/sample/felines/class/index.ts +5 -0
  206. package/sample/felines/class/lion.class.ts +5 -0
  207. package/sample/felines/class/message.class.ts +17 -0
  208. package/sample/felines/class/tiger.class.ts +5 -0
  209. package/sample/felines/dto/create-feline.dto.ts +13 -0
  210. package/sample/felines/dto/index.ts +2 -0
  211. package/sample/felines/dto/journaling-data.dto.ts +5 -0
  212. package/sample/felines/felines.constants.ts +1 -0
  213. package/sample/felines/felines.controller.ts +76 -0
  214. package/sample/felines/felines.gateway.ts +76 -0
  215. package/sample/felines/felines.module.ts +15 -0
  216. package/sample/felines/felines.service.ts +24 -0
  217. package/sample/felines/rto/feline.rto.ts +17 -0
  218. package/sample/felines/rto/index.ts +1 -0
  219. package/sample/index.ts +3 -0
  220. package/sample/main.ts +34 -0
  221. package/src/asyncapi-document.builder.ts +5 -19
  222. package/src/asyncapi.module.ts +5 -16
  223. package/src/decorators/asyncapi-operation-for-meta-key.decorator.ts +4 -16
  224. package/src/decorators/asyncapi-operation.decorator.ts +1 -3
  225. package/src/decorators/asyncapi-pub.decorator.ts +6 -13
  226. package/src/decorators/asyncapi-sub.decorator.ts +6 -13
  227. package/src/explorers/asyncapi-class.explorer.ts +2 -6
  228. package/src/explorers/asyncapi-operation.explorer.ts +3 -12
  229. package/src/interface/asyncapi-common.interfaces.ts +1 -4
  230. package/src/interface/asyncapi-operation-options.interface.ts +2 -4
  231. package/src/interface/asyncapi-operation-payload.interface.ts +1 -5
  232. package/src/interface/asyncapi-server.interface.ts +1 -4
  233. package/src/interface/asyncapi-template-options.interface.ts +5 -5
  234. package/src/interface/denormalized-doc.interface.ts +1 -5
  235. package/src/services/asyncapi.explorer.ts +25 -34
  236. package/src/services/asyncapi.generator.ts +28 -16
  237. package/src/services/asyncapi.scanner.ts +39 -89
  238. package/src/services/asyncapi.transformer.ts +2 -8
  239. package/src/services/index.ts +0 -1
  240. package/test/__mocks__/@asyncapi/generator.js +41 -0
  241. package/test/configs/jest-base.config.ts +19 -0
  242. package/test/configs/jest-e2e.config.js +7 -0
  243. package/test/configs/jest-e2e.config.ts +19 -0
  244. package/test/configs/jest-swagger-plugin.js +16 -0
  245. package/test/express.e2e-spec.ts +64 -0
  246. package/test/fastify.e2e-spec.ts +65 -0
@@ -12,18 +12,9 @@ export const exploreAsyncApiOperationMetadata = (
12
12
  _prototype: Type<unknown>,
13
13
  method: object,
14
14
  ) => {
15
- const metadataOperations: AsyncApiOperationOptionsRaw[] = Reflect.getMetadata(
16
- DECORATORS.AsyncApiOperation,
17
- method,
18
- );
19
- const metadataSubs: AsyncApiOperationOptionsRaw[] = Reflect.getMetadata(
20
- DECORATORS.AsyncApiSub,
21
- method,
22
- );
23
- const metadataPubs: AsyncApiOperationOptionsRaw[] = Reflect.getMetadata(
24
- DECORATORS.AsyncApiPub,
25
- method,
26
- );
15
+ const metadataOperations: AsyncApiOperationOptionsRaw[] = Reflect.getMetadata(DECORATORS.AsyncApiOperation, method);
16
+ const metadataSubs: AsyncApiOperationOptionsRaw[] = Reflect.getMetadata(DECORATORS.AsyncApiSub, method);
17
+ const metadataPubs: AsyncApiOperationOptionsRaw[] = Reflect.getMetadata(DECORATORS.AsyncApiPub, method);
27
18
 
28
19
  const metadataCombined = [
29
20
  ...(metadataOperations ? Object.values(metadataOperations) : []),
@@ -54,10 +54,7 @@ export interface AsyncComponentsObject {
54
54
  messageTraits?: Record<string, AsyncMessageTraitObject>;
55
55
  serverBindings?: Record<string, KafkaServerBinding | AmqpServerBinding>;
56
56
  channelBindings?: Record<string, KafkaChannelBinding | AmqpChannelBinding>;
57
- operationBindings?: Record<
58
- string,
59
- KafkaOperationBinding | AmqpOperationBinding
60
- >;
57
+ operationBindings?: Record<string, KafkaOperationBinding | AmqpOperationBinding>;
61
58
  messageBindings?: Record<string, KafkaMessageBinding | AmqpMessageBinding>;
62
59
  }
63
60
 
@@ -1,12 +1,10 @@
1
1
  import { AsyncOperationObject } from './asyncapi-common.interfaces';
2
2
  import { AsyncApiMessage } from './asyncapi-message.interface';
3
3
 
4
- export interface AsyncApiSpecificOperationOptions
5
- extends Omit<AsyncOperationObject, 'message'> {
4
+ export interface AsyncApiSpecificOperationOptions extends Omit<AsyncOperationObject, 'message'> {
6
5
  message: AsyncApiMessage;
7
6
  }
8
7
 
9
- export interface AsyncApiOperationOptions
10
- extends AsyncApiSpecificOperationOptions {
8
+ export interface AsyncApiOperationOptions extends AsyncApiSpecificOperationOptions {
11
9
  type: 'pub' | 'sub';
12
10
  }
@@ -1,7 +1,3 @@
1
1
  import { Type } from '@nestjs/common';
2
2
 
3
- export type AsyncOperationPayload =
4
- | Type<unknown>
5
- | Function
6
- | [Function]
7
- | string;
3
+ export type AsyncOperationPayload = Type<unknown> | Function | [Function] | string;
@@ -1,9 +1,6 @@
1
1
  import { ServerObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
2
2
  import { AmqpServerBinding, KafkaServerBinding } from '../binding';
3
- import {
4
- AsyncServerVariableObject,
5
- SecurityObject,
6
- } from './asyncapi-common.interfaces';
3
+ import { AsyncServerVariableObject, SecurityObject } from './asyncapi-common.interfaces';
7
4
 
8
5
  export interface AsyncServerObject extends Omit<ServerObject, 'variables'> {
9
6
  variables?: Record<string, AsyncServerVariableObject>;
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * @see https://github.com/asyncapi/html-template#supported-parameters
3
- **/
3
+ * */
4
4
  export interface AsyncApiTemplateOptions {
5
- /** @default byTagsNoRoot **/
5
+ /** @default byTagsNoRoot * */
6
6
  sidebarOrganization?: 'byTags' | 'byTagsNoRoot';
7
7
  /**
8
8
  * @example /docs
9
- **/
9
+ * */
10
10
  baseHref?: string;
11
- /** @default true **/
11
+ /** @default true * */
12
12
  singleFile?: boolean;
13
- /** @example asyncapi.html **/
13
+ /** @example asyncapi.html * */
14
14
  outFilename?: string;
15
15
  /**
16
16
  * @description Generates output HTML as PDF
@@ -1,8 +1,4 @@
1
- import {
2
- AsyncApiDocument,
3
- AsyncChannelObject,
4
- AsyncOperationObject,
5
- } from './asyncapi-common.interfaces';
1
+ import { AsyncApiDocument, AsyncChannelObject, AsyncOperationObject } from './asyncapi-common.interfaces';
6
2
 
7
3
  export interface DenormalizedDoc extends Partial<AsyncApiDocument> {
8
4
  root?: { name: string } & AsyncChannelObject;
@@ -14,7 +14,9 @@ import { DenormalizedDoc, DenormalizedDocResolvers } from '../interface';
14
14
 
15
15
  export class AsyncApiExplorer {
16
16
  private readonly metadataScanner = new MetadataScanner();
17
+
17
18
  private readonly schemas: SchemaObject[] = [];
19
+
18
20
  private readonly schemaRefsStack: string[] = [];
19
21
 
20
22
  private operationIdFactory = (controllerKey: string, methodKey: string) =>
@@ -34,20 +36,14 @@ export class AsyncApiExplorer {
34
36
  if (
35
37
  !instance ||
36
38
  !metatype ||
37
- !Reflect.getMetadataKeys(metatype).find((label) =>
38
- asyncApiClassAnnotationLabels.includes(label),
39
- )
39
+ !Reflect.getMetadataKeys(metatype).find((label) => asyncApiClassAnnotationLabels.includes(label))
40
40
  ) {
41
41
  return [];
42
42
  }
43
43
 
44
44
  const prototype = Object.getPrototypeOf(instance);
45
45
  const documentResolvers: DenormalizedDocResolvers = {
46
- root: [
47
- exploreAsyncapiClassMetadata,
48
- exploreControllerMetadata,
49
- exploreGatewayMetadata,
50
- ],
46
+ root: [exploreAsyncapiClassMetadata, exploreControllerMetadata, exploreGatewayMetadata],
51
47
  security: [],
52
48
  tags: [],
53
49
  operations: [exploreAsyncApiOperationMetadata],
@@ -76,45 +72,40 @@ export class AsyncApiExplorer {
76
72
  _modulePath?: string,
77
73
  _globalPrefix?: string,
78
74
  ): DenormalizedDoc[] {
79
- const denormalizedAsyncapiServices = this.metadataScanner.scanFromPrototype<
80
- unknown,
81
- DenormalizedDoc[]
82
- >(instance, prototype, (name) => {
83
- const targetCallback = prototype[name];
84
- const methodMetadata = documentResolvers.root.reduce((_metadata, fn) => {
85
- const serviceMetadata = fn(metatype);
75
+ const denormalizedAsyncapiServices = this.metadataScanner.scanFromPrototype<unknown, DenormalizedDoc[]>(
76
+ instance,
77
+ prototype,
78
+ (name) => {
79
+ const targetCallback = prototype[name];
80
+ const methodMetadata = documentResolvers.root.reduce((_metadata, fn) => {
81
+ const serviceMetadata = fn(metatype);
86
82
 
87
- const channels = documentResolvers.operations.reduce(
88
- (operations, exploreOperationsMeta) => {
89
- const meta = exploreOperationsMeta(
90
- this.schemas,
91
- instance,
92
- prototype,
93
- targetCallback,
94
- );
83
+ const channels = documentResolvers.operations.reduce((operationsParam, exploreOperationsMeta) => {
84
+ const operations = operationsParam;
85
+ const meta = exploreOperationsMeta(this.schemas, instance, prototype, targetCallback);
95
86
  if (!meta) {
96
87
  return operations;
97
88
  }
98
89
 
99
90
  meta.forEach((op) => {
100
- if (operations.hasOwnProperty(op.channel)) {
91
+ // @eslint-disable-next-line
92
+ if (Object.prototype.hasOwnProperty.call(operations, op.channel)) {
101
93
  operations[op.channel] = { ...operations[op.channel], ...op };
102
94
  } else {
103
95
  operations[op.channel] = op;
104
96
  }
105
97
  });
106
98
  return operations;
107
- },
108
- {},
109
- );
99
+ }, {});
110
100
 
111
- return Object.keys(channels).map((channel) => ({
112
- root: { ...serviceMetadata, name: channel },
113
- operations: channels[channel],
114
- }));
115
- }, []);
116
- return methodMetadata;
117
- });
101
+ return Object.keys(channels).map((channel) => ({
102
+ root: { ...serviceMetadata, name: channel },
103
+ operations: channels[channel],
104
+ }));
105
+ }, []);
106
+ return methodMetadata;
107
+ },
108
+ );
118
109
 
119
110
  return flatten(denormalizedAsyncapiServices);
120
111
  }
@@ -1,33 +1,45 @@
1
- import Generator from '@asyncapi/generator';
1
+ import fs from 'fs/promises';
2
2
  import jsyaml from 'js-yaml';
3
3
  import os from 'os';
4
- import {
5
- AsyncApiDocument,
6
- AsyncApiTemplateOptions,
7
- GeneratorOptions,
8
- } from '../interface';
4
+ import path from 'path';
5
+ import { AsyncApiDocument, AsyncApiTemplateOptions, GeneratorOptions } from '../interface';
9
6
 
10
7
  export class AsyncapiGenerator {
11
- private readonly generator: GeneratorOptions;
8
+ private readonly templateOptions?: AsyncApiTemplateOptions;
12
9
 
13
- constructor(readonly templateOptions?: AsyncApiTemplateOptions) {
14
- this.generator = new Generator('@asyncapi/html-template', os.tmpdir(), {
10
+ constructor(templateOptions?: AsyncApiTemplateOptions) {
11
+ this.templateOptions = templateOptions;
12
+ }
13
+
14
+ public async generate(contract: AsyncApiDocument): Promise<string> {
15
+ // Lazy-load @asyncapi/generator to avoid loading it at module initialization time.
16
+ // This allows tests and code that only use decorators to work without
17
+ // requiring the full generator dependency tree (which has nested semver issues in Jest).
18
+ const { default: Generator } = await import('@asyncapi/generator');
19
+
20
+ const yaml = jsyaml.dump(contract);
21
+ const tmpDir = path.join(os.tmpdir(), `asyncapi-${Date.now()}-${Math.random().toString(36).slice(2)}`);
22
+
23
+ const generator: GeneratorOptions = new Generator('@asyncapi/html-template', tmpDir, {
15
24
  forceWrite: true,
16
- entrypoint: 'index.html',
17
- output: 'string',
18
25
  templateParams: {
19
26
  singleFile: true,
20
- ...templateOptions,
27
+ ...this.templateOptions,
21
28
  },
22
29
  });
23
- }
24
30
 
25
- public async generate(contract: AsyncApiDocument): Promise<string> {
26
- const yaml = jsyaml.dump(contract);
27
- return this.generator.generateFromString(yaml, {
31
+ await generator.generateFromString(yaml, {
28
32
  resolve: {
29
33
  file: false,
30
34
  },
31
35
  });
36
+
37
+ const htmlPath = path.join(tmpDir, 'index.html');
38
+ const html = await fs.readFile(htmlPath, 'utf-8');
39
+
40
+ // Clean up temp directory
41
+ await fs.rm(tmpDir, { recursive: true, force: true }).catch(() => {});
42
+
43
+ return html;
32
44
  }
33
45
  }
@@ -1,7 +1,6 @@
1
1
  import { INestApplicationContext, Type } from '@nestjs/common';
2
2
  import { MODULE_PATH } from '@nestjs/common/constants';
3
3
  import { Injectable, InjectionToken } from '@nestjs/common/interfaces';
4
- import { NestContainer } from '@nestjs/core/injector/container';
5
4
  import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
6
5
  import { Module } from '@nestjs/core/injector/module';
7
6
  import { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
@@ -10,23 +9,20 @@ import { SchemaObjectFactory } from '@nestjs/swagger/dist/services/schema-object
10
9
  import { SwaggerTypesMapper } from '@nestjs/swagger/dist/services/swagger-types-mapper';
11
10
  import { stripLastSlash } from '@nestjs/swagger/dist/utils/strip-last-slash.util';
12
11
  import { flatten, isEmpty } from 'lodash';
13
- import {
14
- AsyncApiDocument,
15
- AsyncApiDocumentOptions,
16
- DenormalizedDoc,
17
- } from '../interface';
12
+ import { AsyncApiDocument, AsyncApiDocumentOptions, DenormalizedDoc } from '../interface';
18
13
  import { AsyncApiExplorer } from './asyncapi.explorer';
19
14
  import { AsyncapiTransformer } from './asyncapi.transformer';
20
15
 
21
16
  export class AsyncapiScanner {
22
17
  private readonly transformer = new AsyncapiTransformer();
18
+
23
19
  private readonly explorer = new AsyncApiExplorer();
20
+
24
21
  private readonly modelPropertiesAccessor = new ModelPropertiesAccessor();
22
+
25
23
  private readonly swaggerTypesMapper = new SwaggerTypesMapper();
26
- private readonly schemaObjectFactory = new SchemaObjectFactory(
27
- this.modelPropertiesAccessor,
28
- this.swaggerTypesMapper,
29
- );
24
+
25
+ private readonly schemaObjectFactory = new SchemaObjectFactory(this.modelPropertiesAccessor, this.swaggerTypesMapper);
30
26
 
31
27
  public scanApplication(
32
28
  app: INestApplicationContext,
@@ -40,65 +36,35 @@ export class AsyncapiScanner {
40
36
  operationIdFactory,
41
37
  } = options;
42
38
 
43
- const container: NestContainer = (app as any).container;
44
- const modules: Module[] = this.getModules(
45
- container.getModules(),
46
- includedModules,
47
- );
48
- const globalPrefix = !ignoreGlobalPrefix
49
- ? stripLastSlash(this.getGlobalPrefix(app))
50
- : '';
51
-
52
- const denormalizedChannels = modules.reduce(
53
- (channels, { providers, metatype, imports, controllers }) => {
54
- let allProviders = new Map([...providers, ...controllers]);
55
-
56
- if (deepScanRoutes) {
57
- // only load submodules routes if asked
58
- const isGlobal = (module: Type<any>) =>
59
- !container.isGlobalModule(module);
60
-
61
- Array.from(imports.values())
62
- .filter(isGlobal as any)
63
- .map(
64
- ({
65
- providers: relatedProviders,
66
- controllers: relatedControllers,
67
- }) => ({
68
- relatedProviders,
69
- relatedControllers,
70
- }),
71
- )
72
- .forEach(({ relatedProviders, relatedControllers }) => {
73
- allProviders = new Map([
74
- ...allProviders,
75
- ...relatedProviders,
76
- ...relatedControllers,
77
- ]);
78
- });
79
- }
80
- const path = metatype
81
- ? Reflect.getMetadata(MODULE_PATH, metatype)
82
- : undefined;
83
-
84
- return [
85
- ...channels,
86
- ...this.scanModuleProviders(
87
- allProviders,
88
- path,
89
- globalPrefix,
90
- operationIdFactory,
91
- ),
92
- ];
93
- },
94
- [],
95
- );
39
+ const { container } = app as any;
40
+ const modules: Module[] = this.getModules(container.getModules(), includedModules);
41
+ const globalPrefix = !ignoreGlobalPrefix ? stripLastSlash(this.getGlobalPrefix(app)) : '';
42
+
43
+ const denormalizedChannels = modules.reduce((channels, { providers, metatype, imports, controllers }) => {
44
+ let allProviders = new Map([...providers, ...controllers]);
45
+
46
+ if (deepScanRoutes) {
47
+ // only load submodules routes if asked
48
+ const isGlobal = (module: Type<any>) => !container.isGlobalModule(module);
49
+
50
+ Array.from(imports.values())
51
+ .filter(isGlobal as any)
52
+ .map(({ providers: relatedProviders, controllers: relatedControllers }) => ({
53
+ relatedProviders,
54
+ relatedControllers,
55
+ }))
56
+ .forEach(({ relatedProviders, relatedControllers }) => {
57
+ allProviders = new Map([...allProviders, ...relatedProviders, ...relatedControllers]);
58
+ });
59
+ }
60
+ const path = metatype ? Reflect.getMetadata(MODULE_PATH, metatype) : undefined;
61
+
62
+ return [...channels, ...this.scanModuleProviders(allProviders, path, globalPrefix, operationIdFactory)];
63
+ }, []);
96
64
 
97
65
  const schemas = this.explorer.getSchemas();
98
66
  this.addExtraModels(schemas, extraModels);
99
- const normalizedChannels = this.transformer.normalizeChannels(
100
- flatten(denormalizedChannels),
101
- );
67
+ const normalizedChannels = this.transformer.normalizeChannels(flatten(denormalizedChannels));
102
68
  return {
103
69
  ...normalizedChannels,
104
70
  components: { schemas },
@@ -111,32 +77,19 @@ export class AsyncapiScanner {
111
77
  globalPrefix?: string,
112
78
  operationIdFactory?: (controllerKey: string, methodKey: string) => string,
113
79
  ): DenormalizedDoc[] {
114
- const denormalizedArray = [...providers.values()].reduce(
115
- (denormalized, prov) => {
116
- const object = this.explorer.explorerAsyncapiServices(
117
- prov,
118
- modulePath,
119
- globalPrefix,
120
- operationIdFactory,
121
- );
122
- return [...denormalized, ...object];
123
- },
124
- [],
125
- );
80
+ const denormalizedArray = [...providers.values()].reduce((denormalized, prov) => {
81
+ const object = this.explorer.explorerAsyncapiServices(prov, modulePath, globalPrefix, operationIdFactory);
82
+ return [...denormalized, ...object];
83
+ }, []);
126
84
 
127
85
  return flatten(denormalizedArray) as any;
128
86
  }
129
87
 
130
- private getModules(
131
- modulesContainer: Map<string, Module>,
132
- include: Function[],
133
- ): Module[] {
88
+ private getModules(modulesContainer: Map<string, Module>, include: Function[]): Module[] {
134
89
  if (!include || isEmpty(include)) {
135
90
  return [...modulesContainer.values()];
136
91
  }
137
- return [...modulesContainer.values()].filter(({ metatype }) =>
138
- include.some((item) => item === metatype),
139
- );
92
+ return [...modulesContainer.values()].filter(({ metatype }) => include.some((item) => item === metatype));
140
93
  }
141
94
 
142
95
  private getGlobalPrefix(app: INestApplicationContext): string {
@@ -144,10 +97,7 @@ export class AsyncapiScanner {
144
97
  return internalConfigRef?.getGlobalPrefix() || '';
145
98
  }
146
99
 
147
- private addExtraModels(
148
- schemas: Record<string, SchemaObject>,
149
- extraModels: Function[],
150
- ) {
100
+ private addExtraModels(schemas: Record<string, SchemaObject>, extraModels: Function[]) {
151
101
  extraModels.forEach((item) => {
152
102
  this.schemaObjectFactory.exploreModelSchema(item, schemas);
153
103
  });
@@ -1,13 +1,7 @@
1
- import {
2
- AsyncChannelObject,
3
- AsyncChannelsObject,
4
- DenormalizedDoc,
5
- } from '../interface';
1
+ import { AsyncChannelObject, AsyncChannelsObject, DenormalizedDoc } from '../interface';
6
2
 
7
3
  export class AsyncapiTransformer {
8
- public normalizeChannels(
9
- denormalizedDocs: DenormalizedDoc[],
10
- ): Record<'channels', AsyncChannelsObject> {
4
+ public normalizeChannels(denormalizedDocs: DenormalizedDoc[]): Record<'channels', AsyncChannelsObject> {
11
5
  const flatChannels = denormalizedDocs.map((doc: DenormalizedDoc) => {
12
6
  const key = doc.root?.name;
13
7
  const value: AsyncChannelObject = {
@@ -2,4 +2,3 @@ export * from './asyncapi.generator';
2
2
  export * from './operation-object.factory';
3
3
  export * from './asyncapi.explorer';
4
4
  export * from './asyncapi.scanner';
5
- export * from './asyncapi.scanner';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Mock for @asyncapi/generator to avoid ESM issues in Jest tests.
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ class MockGenerator {
9
+ constructor(templateName, targetDir, options = {}) {
10
+ this.templateName = templateName;
11
+ this.targetDir = targetDir;
12
+ this.options = options;
13
+ }
14
+
15
+ async generateFromString(yaml) {
16
+ const html = `<!DOCTYPE html>
17
+ <html>
18
+ <head>
19
+ <title>AsyncAPI Documentation</title>
20
+ </head>
21
+ <body>
22
+ <h1>AsyncAPI Documentation</h1>
23
+ <p>Mock HTML generated for testing</p>
24
+ <pre>${yaml.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</pre>
25
+ </body>
26
+ </html>`;
27
+
28
+ // Create the target directory and write the file
29
+ fs.mkdirSync(this.targetDir, { recursive: true });
30
+ fs.writeFileSync(path.join(this.targetDir, 'index.html'), html, 'utf-8');
31
+
32
+ return html;
33
+ }
34
+
35
+ async generate(asyncapi) {
36
+ return this.generateFromString(JSON.stringify(asyncapi, null, 2));
37
+ }
38
+ }
39
+
40
+ module.exports = MockGenerator;
41
+ module.exports.default = MockGenerator;
@@ -0,0 +1,19 @@
1
+ import { JestConfigWithTsJest } from 'ts-jest';
2
+
3
+ export const baseConfig: JestConfigWithTsJest = {
4
+ moduleFileExtensions: ['js', 'json', 'ts'],
5
+ rootDir: '../../',
6
+ transform: {
7
+ '^.+\\.(t|j)s$': 'ts-jest',
8
+ },
9
+ collectCoverageFrom: ['**/*.(t|j)s'],
10
+ coverageDirectory: './coverage',
11
+ testEnvironment: 'node',
12
+ moduleNameMapper: {
13
+ '#lib(|/.*)$': '<rootDir>/lib/$1',
14
+ '#sample(|/.*)$': '<rootDir>/sample/$1',
15
+ '#test(|/.*)$': '<rootDir>/test/$1',
16
+ },
17
+ };
18
+
19
+ export default baseConfig;
@@ -0,0 +1,7 @@
1
+ // Forzamos ts-node desde aquí antes de importar la config real
2
+ require('ts-node').register({
3
+ transpileOnly: true,
4
+ });
5
+
6
+ // Si usas `export default` en tu config.ts:
7
+ module.exports = require('./jest-e2e.config.ts').default;
@@ -0,0 +1,19 @@
1
+ import { JestConfigWithTsJest } from 'ts-jest';
2
+ import { baseConfig } from './jest-base.config';
3
+
4
+ export const e2eConfig: JestConfigWithTsJest = {
5
+ ...baseConfig,
6
+ testRegex: '.\\.e2e-spec\\.ts$',
7
+ transform: {
8
+ '^.+\\.(t|j)s$': [
9
+ 'ts-jest',
10
+ {
11
+ astTransformers: {
12
+ before: ['test/configs/jest-swagger-plugin.js'],
13
+ },
14
+ },
15
+ ],
16
+ },
17
+ };
18
+
19
+ export default e2eConfig;
@@ -0,0 +1,16 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
2
+ const transformer = require('@nestjs/swagger/plugin');
3
+
4
+ module.exports.name = 'nestjs-swagger-transformer';
5
+ // you should change the version number anytime you change the configuration below - otherwise, jest will not detect changes
6
+ module.exports.version = 1;
7
+
8
+ module.exports.factory = (cs) => {
9
+ return transformer.before(
10
+ {
11
+ introspectComments: true,
12
+ dtoFileNameSuffix: ['.dto.ts', '.class.ts'],
13
+ },
14
+ cs.program, // "cs.tsCompiler.program" for older versions of Jest (<= v27)
15
+ );
16
+ };
@@ -0,0 +1,64 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+ import { ExpressAdapter, NestExpressApplication } from '@nestjs/platform-express';
3
+ import * as fs from 'fs/promises';
4
+ import jsyaml from 'js-yaml';
5
+ import request from 'supertest';
6
+ import { AsyncApiModule } from '../src';
7
+ import { AppModule } from '../sample/app.module';
8
+ import { makeAsyncapiDocument } from '../sample/common';
9
+ import { DOC_RELATIVE_PATH } from '../sample/constants';
10
+
11
+ describe('Express AsyncAPI', () => {
12
+ let app: NestExpressApplication;
13
+
14
+ beforeAll(async () => {
15
+ app = await NestFactory.create<NestExpressApplication>(AppModule, new ExpressAdapter(), { logger: false });
16
+ const asyncapiDocument = await makeAsyncapiDocument(app);
17
+ await AsyncApiModule.setup(DOC_RELATIVE_PATH, app, asyncapiDocument);
18
+
19
+ await app.init();
20
+ });
21
+
22
+ it('should serve doc html', async () => {
23
+ const { text } = await request(app.getHttpServer())
24
+ .get(DOC_RELATIVE_PATH)
25
+ .expect(200)
26
+ .expect('Content-Type', /text\/html/);
27
+ const htmlSample = await fs.readFile('./misc/references/ref.html', {
28
+ encoding: 'utf8',
29
+ });
30
+ expect(text).toEqual(htmlSample);
31
+ });
32
+
33
+ it('should serve doc json', async () => {
34
+ const { text } = await request(app.getHttpServer())
35
+ .get(`${DOC_RELATIVE_PATH}-json`)
36
+ .expect(200)
37
+ .expect('Content-Type', /application\/json/);
38
+
39
+ const jsonFetched = JSON.parse(text);
40
+ const jsonReferenceSample = JSON.parse(
41
+ await fs.readFile('./misc/references/ref.json', {
42
+ encoding: 'utf8',
43
+ }),
44
+ );
45
+
46
+ expect(jsonFetched).toEqual(jsonReferenceSample);
47
+ });
48
+
49
+ it('should serve doc yaml', async () => {
50
+ const { text } = await request(app.getHttpServer())
51
+ .get(`${DOC_RELATIVE_PATH}-yaml`)
52
+ .expect(200)
53
+ .expect('Content-Type', /text\/yaml/);
54
+
55
+ const yamlFetched = jsyaml.load(text);
56
+ const yamlReferenceSample = jsyaml.load(
57
+ await fs.readFile('./misc/references/ref.yaml', {
58
+ encoding: 'utf8',
59
+ }),
60
+ );
61
+
62
+ expect(yamlFetched).toEqual(yamlReferenceSample);
63
+ });
64
+ });