@geekmidas/events 0.1.0 → 1.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 (240) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{BasicConnection-C4_Y0nIj.d.mts → BasicConnection-BTRsXiuH.d.cts} +3 -2
  3. package/dist/BasicConnection-BTRsXiuH.d.cts.map +1 -0
  4. package/dist/BasicConnection-Bz3F2rXA.cjs.map +1 -1
  5. package/dist/{BasicConnection-CddnULuT.d.cts → BasicConnection-DIxtA7jK.d.mts} +3 -2
  6. package/dist/BasicConnection-DIxtA7jK.d.mts.map +1 -0
  7. package/dist/BasicConnection-DOSOFSsW.mjs.map +1 -1
  8. package/dist/BasicPublisher-CaX8GCUi.mjs.map +1 -1
  9. package/dist/{BasicPublisher-DCBxaTwC.d.mts → BasicPublisher-CpJFK2oS.d.mts} +5 -3
  10. package/dist/BasicPublisher-CpJFK2oS.d.mts.map +1 -0
  11. package/dist/{BasicPublisher-V5hELHG9.d.cts → BasicPublisher-D1q0a2xp.d.cts} +5 -3
  12. package/dist/BasicPublisher-D1q0a2xp.d.cts.map +1 -0
  13. package/dist/BasicPublisher-afwuA4Xp.cjs.map +1 -1
  14. package/dist/{BasicSubscriber-BB3aKeph.d.mts → BasicSubscriber-6w0Af4DD.d.mts} +5 -3
  15. package/dist/BasicSubscriber-6w0Af4DD.d.mts.map +1 -0
  16. package/dist/{BasicSubscriber-Ckf3BkxS.cjs → BasicSubscriber-BN9fKRrU.cjs} +2 -4
  17. package/dist/BasicSubscriber-BN9fKRrU.cjs.map +1 -0
  18. package/dist/{BasicSubscriber-DPpJNUUY.mjs → BasicSubscriber-BgsKYQmi.mjs} +2 -4
  19. package/dist/BasicSubscriber-BgsKYQmi.mjs.map +1 -0
  20. package/dist/{BasicSubscriber-DOwgFw5j.d.cts → BasicSubscriber-DXxQewjc.d.cts} +5 -3
  21. package/dist/BasicSubscriber-DXxQewjc.d.cts.map +1 -0
  22. package/dist/{EventConnection-DZhn1Msl.d.cts → EventConnection-BaJRXdg7.d.cts} +2 -2
  23. package/dist/EventConnection-BaJRXdg7.d.cts.map +1 -0
  24. package/dist/EventConnection-ClLMoIUU.mjs.map +1 -1
  25. package/dist/EventConnection-F_N81SXu.cjs.map +1 -1
  26. package/dist/{EventConnection-DeXfLGom.d.mts → EventConnection-SOhtmxib.d.mts} +2 -2
  27. package/dist/EventConnection-SOhtmxib.d.mts.map +1 -0
  28. package/dist/EventConnection.d.cts +2 -2
  29. package/dist/EventConnection.d.mts +2 -2
  30. package/dist/{Publisher-C3m8JFXm.d.mts → Publisher-1CfcYRnm.d.mts} +4 -2
  31. package/dist/Publisher-1CfcYRnm.d.mts.map +1 -0
  32. package/dist/{Publisher-nXNVjL0e.d.cts → Publisher-BfNgvHwQ.d.cts} +4 -2
  33. package/dist/Publisher-BfNgvHwQ.d.cts.map +1 -0
  34. package/dist/Publisher-DS0aOh8r.mjs.map +1 -1
  35. package/dist/Publisher-ceu6OxCj.cjs.map +1 -1
  36. package/dist/Publisher.d.cts +2 -2
  37. package/dist/Publisher.d.mts +2 -2
  38. package/dist/{RabbitMQConnection-7cWp_-_Q.d.cts → RabbitMQConnection-Bm8VCI2R.d.cts} +3 -2
  39. package/dist/RabbitMQConnection-Bm8VCI2R.d.cts.map +1 -0
  40. package/dist/{RabbitMQConnection-BN_GybOv.cjs → RabbitMQConnection-CjBoCd9n.cjs} +2 -2
  41. package/dist/RabbitMQConnection-CjBoCd9n.cjs.map +1 -0
  42. package/dist/{RabbitMQConnection-C2HC7J7K.d.mts → RabbitMQConnection-Ck-ktCp_.d.mts} +3 -2
  43. package/dist/RabbitMQConnection-Ck-ktCp_.d.mts.map +1 -0
  44. package/dist/{RabbitMQConnection-eVOVS4Dl.mjs → RabbitMQConnection-DFLkr2vq.mjs} +2 -2
  45. package/dist/RabbitMQConnection-DFLkr2vq.mjs.map +1 -0
  46. package/dist/{RabbitMQPublisher-CSCn6gMA.cjs → RabbitMQPublisher-C94q6g8q.cjs} +1 -1
  47. package/dist/RabbitMQPublisher-C94q6g8q.cjs.map +1 -0
  48. package/dist/{RabbitMQPublisher-CpcbgrXj.d.cts → RabbitMQPublisher-COnxcWEZ.d.mts} +5 -3
  49. package/dist/RabbitMQPublisher-COnxcWEZ.d.mts.map +1 -0
  50. package/dist/{RabbitMQPublisher-DDZi15Ro.d.mts → RabbitMQPublisher-qQT7-1-p.d.cts} +5 -3
  51. package/dist/RabbitMQPublisher-qQT7-1-p.d.cts.map +1 -0
  52. package/dist/{RabbitMQPublisher-BZX0edgL.mjs → RabbitMQPublisher-yUqWNq-P.mjs} +1 -1
  53. package/dist/RabbitMQPublisher-yUqWNq-P.mjs.map +1 -0
  54. package/dist/{RabbitMQSubscriber-B5I-hBaT.mjs → RabbitMQSubscriber-BWniDtFs.mjs} +2 -3
  55. package/dist/RabbitMQSubscriber-BWniDtFs.mjs.map +1 -0
  56. package/dist/{RabbitMQSubscriber-BJ9PTGny.d.cts → RabbitMQSubscriber-C_sZjDPU.d.mts} +5 -3
  57. package/dist/RabbitMQSubscriber-C_sZjDPU.d.mts.map +1 -0
  58. package/dist/{RabbitMQSubscriber-CIvIw8MG.d.mts → RabbitMQSubscriber-D12sssMb.d.cts} +5 -3
  59. package/dist/RabbitMQSubscriber-D12sssMb.d.cts.map +1 -0
  60. package/dist/{RabbitMQSubscriber-Cj7qLIi6.cjs → RabbitMQSubscriber-DGEO-clQ.cjs} +2 -3
  61. package/dist/RabbitMQSubscriber-DGEO-clQ.cjs.map +1 -0
  62. package/dist/{SNSConnection-CAImaMM2.d.mts → SNSConnection-BDfQrVBO.d.cts} +3 -2
  63. package/dist/SNSConnection-BDfQrVBO.d.cts.map +1 -0
  64. package/dist/{SNSConnection-DVEE3XoC.mjs → SNSConnection-D8a31sJK.mjs} +1 -1
  65. package/dist/SNSConnection-D8a31sJK.mjs.map +1 -0
  66. package/dist/{SNSConnection-CZz_U56V.cjs → SNSConnection-DqLbiQ_X.cjs} +1 -1
  67. package/dist/SNSConnection-DqLbiQ_X.cjs.map +1 -0
  68. package/dist/{SNSConnection-Cqbr7-Ho.d.cts → SNSConnection-mHb10nQ6.d.mts} +3 -2
  69. package/dist/SNSConnection-mHb10nQ6.d.mts.map +1 -0
  70. package/dist/{SNSPublisher-DbWVH-g_.cjs → SNSPublisher-BRgwumLS.cjs} +2 -3
  71. package/dist/SNSPublisher-BRgwumLS.cjs.map +1 -0
  72. package/dist/{SNSPublisher-Dkhb4LU-.d.mts → SNSPublisher-CM7UAWtf.d.mts} +7 -6
  73. package/dist/SNSPublisher-CM7UAWtf.d.mts.map +1 -0
  74. package/dist/{SNSPublisher-Do-FMUyT.mjs → SNSPublisher-i4w_-RmP.mjs} +2 -3
  75. package/dist/SNSPublisher-i4w_-RmP.mjs.map +1 -0
  76. package/dist/{SNSPublisher-RVZFa7kL.d.cts → SNSPublisher-kpsSlGDn.d.cts} +7 -6
  77. package/dist/SNSPublisher-kpsSlGDn.d.cts.map +1 -0
  78. package/dist/{SNSSubscriber-e7eG48yJ.mjs → SNSSubscriber-BEf-H4p5.mjs} +5 -9
  79. package/dist/SNSSubscriber-BEf-H4p5.mjs.map +1 -0
  80. package/dist/{SNSSubscriber-B31JmkAE.d.mts → SNSSubscriber-DDB3--pm.d.cts} +4 -3
  81. package/dist/SNSSubscriber-DDB3--pm.d.cts.map +1 -0
  82. package/dist/{SNSSubscriber-k2UwKUxu.d.cts → SNSSubscriber-Dwo8391f.d.mts} +4 -3
  83. package/dist/SNSSubscriber-Dwo8391f.d.mts.map +1 -0
  84. package/dist/{SNSSubscriber-DpEwQ8Tb.cjs → SNSSubscriber-FYqSi346.cjs} +5 -9
  85. package/dist/SNSSubscriber-FYqSi346.cjs.map +1 -0
  86. package/dist/{SQSConnection-C5WFqZW7.d.mts → SQSConnection-B5Ep9Tma.d.cts} +3 -2
  87. package/dist/SQSConnection-B5Ep9Tma.d.cts.map +1 -0
  88. package/dist/{SQSConnection-geav2Rsn.d.cts → SQSConnection-BI_xOzyJ.d.mts} +3 -2
  89. package/dist/SQSConnection-BI_xOzyJ.d.mts.map +1 -0
  90. package/dist/{SQSConnection-BBlDWZLU.mjs → SQSConnection-BwHhXLIu.mjs} +1 -1
  91. package/dist/SQSConnection-BwHhXLIu.mjs.map +1 -0
  92. package/dist/{SQSConnection-DWYDtftQ.cjs → SQSConnection-Ctu3XY9E.cjs} +1 -1
  93. package/dist/SQSConnection-Ctu3XY9E.cjs.map +1 -0
  94. package/dist/{SQSPublisher-B3lS2zbu.mjs → SQSPublisher-BDGMw1uf.mjs} +1 -1
  95. package/dist/SQSPublisher-BDGMw1uf.mjs.map +1 -0
  96. package/dist/{SQSPublisher-CdsayOLV.d.mts → SQSPublisher-BN1ds6SN.d.mts} +5 -3
  97. package/dist/SQSPublisher-BN1ds6SN.d.mts.map +1 -0
  98. package/dist/{SQSPublisher-nzbSaXSH.cjs → SQSPublisher-C3Y_gOjV.cjs} +1 -1
  99. package/dist/SQSPublisher-C3Y_gOjV.cjs.map +1 -0
  100. package/dist/{SQSPublisher-Utb_FTe5.d.cts → SQSPublisher-DFTqwzRW.d.cts} +5 -3
  101. package/dist/SQSPublisher-DFTqwzRW.d.cts.map +1 -0
  102. package/dist/{SQSSubscriber-DcL9BbG1.cjs → SQSSubscriber-BB9TWyLA.cjs} +5 -12
  103. package/dist/SQSSubscriber-BB9TWyLA.cjs.map +1 -0
  104. package/dist/{SQSSubscriber-B6iL1EnX.d.cts → SQSSubscriber-BO2me_gs.d.cts} +4 -3
  105. package/dist/SQSSubscriber-BO2me_gs.d.cts.map +1 -0
  106. package/dist/{SQSSubscriber-DBeE4XAR.d.mts → SQSSubscriber-FyZFfr2-.d.mts} +4 -3
  107. package/dist/SQSSubscriber-FyZFfr2-.d.mts.map +1 -0
  108. package/dist/{SQSSubscriber-84pFnW1z.mjs → SQSSubscriber-ux7QEMdd.mjs} +5 -12
  109. package/dist/SQSSubscriber-ux7QEMdd.mjs.map +1 -0
  110. package/dist/Subscriber-Bd9hb_jp.mjs.map +1 -1
  111. package/dist/{Subscriber-Cff9EIpv.d.mts → Subscriber-BxT-KzFJ.d.mts} +4 -2
  112. package/dist/Subscriber-BxT-KzFJ.d.mts.map +1 -0
  113. package/dist/{Subscriber-CpAwcUzb.d.cts → Subscriber-DDD_4Nlx.d.cts} +4 -2
  114. package/dist/Subscriber-DDD_4Nlx.d.cts.map +1 -0
  115. package/dist/Subscriber-saVOWuMq.cjs.map +1 -1
  116. package/dist/Subscriber.d.cts +2 -2
  117. package/dist/Subscriber.d.mts +2 -2
  118. package/dist/basic/BasicConnection.d.cts +2 -2
  119. package/dist/basic/BasicConnection.d.mts +2 -2
  120. package/dist/basic/BasicPublisher.d.cts +3 -3
  121. package/dist/basic/BasicPublisher.d.mts +3 -3
  122. package/dist/basic/BasicSubscriber.cjs +1 -1
  123. package/dist/basic/BasicSubscriber.d.cts +3 -3
  124. package/dist/basic/BasicSubscriber.d.mts +3 -3
  125. package/dist/basic/BasicSubscriber.mjs +1 -1
  126. package/dist/basic/index.cjs +1 -1
  127. package/dist/basic/index.d.cts +4 -4
  128. package/dist/basic/index.d.mts +4 -4
  129. package/dist/basic/index.mjs +1 -1
  130. package/dist/index.d.cts +4 -4
  131. package/dist/index.d.mts +4 -4
  132. package/dist/rabbitmq/RabbitMQConnection.cjs +1 -1
  133. package/dist/rabbitmq/RabbitMQConnection.d.cts +2 -2
  134. package/dist/rabbitmq/RabbitMQConnection.d.mts +2 -2
  135. package/dist/rabbitmq/RabbitMQConnection.mjs +1 -1
  136. package/dist/rabbitmq/RabbitMQPublisher.cjs +1 -1
  137. package/dist/rabbitmq/RabbitMQPublisher.d.cts +3 -3
  138. package/dist/rabbitmq/RabbitMQPublisher.d.mts +3 -3
  139. package/dist/rabbitmq/RabbitMQPublisher.mjs +1 -1
  140. package/dist/rabbitmq/RabbitMQSubscriber.cjs +1 -1
  141. package/dist/rabbitmq/RabbitMQSubscriber.d.cts +3 -3
  142. package/dist/rabbitmq/RabbitMQSubscriber.d.mts +3 -3
  143. package/dist/rabbitmq/RabbitMQSubscriber.mjs +1 -1
  144. package/dist/rabbitmq/index.cjs +3 -3
  145. package/dist/rabbitmq/index.d.cts +4 -4
  146. package/dist/rabbitmq/index.d.mts +4 -4
  147. package/dist/rabbitmq/index.mjs +3 -3
  148. package/dist/sns/SNSConnection.cjs +1 -1
  149. package/dist/sns/SNSConnection.d.cts +2 -2
  150. package/dist/sns/SNSConnection.d.mts +2 -2
  151. package/dist/sns/SNSConnection.mjs +1 -1
  152. package/dist/sns/SNSPublisher.cjs +1 -1
  153. package/dist/sns/SNSPublisher.d.cts +3 -3
  154. package/dist/sns/SNSPublisher.d.mts +3 -3
  155. package/dist/sns/SNSPublisher.mjs +1 -1
  156. package/dist/sns/SNSSubscriber.cjs +3 -3
  157. package/dist/sns/SNSSubscriber.d.cts +3 -3
  158. package/dist/sns/SNSSubscriber.d.mts +3 -3
  159. package/dist/sns/SNSSubscriber.mjs +3 -3
  160. package/dist/sns/index.cjs +5 -5
  161. package/dist/sns/index.d.cts +4 -4
  162. package/dist/sns/index.d.mts +4 -4
  163. package/dist/sns/index.mjs +5 -5
  164. package/dist/sqs/SQSConnection.cjs +1 -1
  165. package/dist/sqs/SQSConnection.d.cts +2 -2
  166. package/dist/sqs/SQSConnection.d.mts +2 -2
  167. package/dist/sqs/SQSConnection.mjs +1 -1
  168. package/dist/sqs/SQSPublisher.cjs +1 -1
  169. package/dist/sqs/SQSPublisher.d.cts +3 -3
  170. package/dist/sqs/SQSPublisher.d.mts +3 -3
  171. package/dist/sqs/SQSPublisher.mjs +1 -1
  172. package/dist/sqs/SQSSubscriber.cjs +1 -1
  173. package/dist/sqs/SQSSubscriber.d.cts +3 -3
  174. package/dist/sqs/SQSSubscriber.d.mts +3 -3
  175. package/dist/sqs/SQSSubscriber.mjs +1 -1
  176. package/dist/sqs/index.cjs +3 -3
  177. package/dist/sqs/index.d.cts +4 -4
  178. package/dist/sqs/index.d.mts +4 -4
  179. package/dist/sqs/index.mjs +3 -3
  180. package/dist/types-B63qHVOG.mjs.map +1 -1
  181. package/dist/types-CvSMEO5z.cjs.map +1 -1
  182. package/dist/{types-D0g-o6wX.d.mts → types-DSVsPgKd.d.mts} +2 -1
  183. package/dist/types-DSVsPgKd.d.mts.map +1 -0
  184. package/dist/{types-DEGUg5BA.d.cts → types-Dmbp1U-6.d.cts} +2 -1
  185. package/dist/types-Dmbp1U-6.d.cts.map +1 -0
  186. package/dist/types.d.cts +1 -1
  187. package/dist/types.d.mts +1 -1
  188. package/package.json +3 -3
  189. package/src/EventConnection.ts +31 -31
  190. package/src/Publisher.ts +78 -78
  191. package/src/Subscriber.ts +91 -91
  192. package/src/__tests__/EventConnection.spec.ts +42 -0
  193. package/src/__tests__/Publisher.spec.ts +96 -0
  194. package/src/__tests__/Subscriber.spec.ts +91 -23
  195. package/src/basic/BasicConnection.ts +35 -35
  196. package/src/basic/BasicPublisher.ts +8 -8
  197. package/src/basic/BasicSubscriber.ts +13 -16
  198. package/src/basic/__tests__/BasicSubscriber.spec.ts +116 -116
  199. package/src/index.ts +13 -13
  200. package/src/rabbitmq/RabbitMQConnection.ts +139 -139
  201. package/src/rabbitmq/RabbitMQPublisher.ts +48 -48
  202. package/src/rabbitmq/RabbitMQSubscriber.ts +100 -102
  203. package/src/rabbitmq/__tests__/RabbitMQPublisher.spec.ts +364 -364
  204. package/src/rabbitmq/__tests__/RabbitMQSubscriber.spec.ts +204 -204
  205. package/src/rabbitmq/index.ts +3 -3
  206. package/src/sns/SNSConnection.ts +76 -76
  207. package/src/sns/SNSPublisher.ts +54 -56
  208. package/src/sns/SNSSubscriber.ts +187 -191
  209. package/src/sns/__tests__/SNS-SQS-Integration.spec.ts +321 -321
  210. package/src/sns/__tests__/SNSPublisher.spec.ts +449 -0
  211. package/src/sns/__tests__/SNSSubscriber.spec.ts +527 -0
  212. package/src/sns/index.ts +3 -3
  213. package/src/sqs/SQSConnection.ts +75 -75
  214. package/src/sqs/SQSPublisher.ts +93 -93
  215. package/src/sqs/SQSSubscriber.ts +157 -165
  216. package/src/sqs/__tests__/SQSPublisher.spec.ts +178 -178
  217. package/src/sqs/index.ts +3 -3
  218. package/src/types.ts +28 -29
  219. package/tsconfig.json +9 -0
  220. package/tsdown.config.ts +1 -1
  221. package/dist/BasicSubscriber-Ckf3BkxS.cjs.map +0 -1
  222. package/dist/BasicSubscriber-DPpJNUUY.mjs.map +0 -1
  223. package/dist/RabbitMQConnection-BN_GybOv.cjs.map +0 -1
  224. package/dist/RabbitMQConnection-eVOVS4Dl.mjs.map +0 -1
  225. package/dist/RabbitMQPublisher-BZX0edgL.mjs.map +0 -1
  226. package/dist/RabbitMQPublisher-CSCn6gMA.cjs.map +0 -1
  227. package/dist/RabbitMQSubscriber-B5I-hBaT.mjs.map +0 -1
  228. package/dist/RabbitMQSubscriber-Cj7qLIi6.cjs.map +0 -1
  229. package/dist/SNSConnection-CZz_U56V.cjs.map +0 -1
  230. package/dist/SNSConnection-DVEE3XoC.mjs.map +0 -1
  231. package/dist/SNSPublisher-DbWVH-g_.cjs.map +0 -1
  232. package/dist/SNSPublisher-Do-FMUyT.mjs.map +0 -1
  233. package/dist/SNSSubscriber-DpEwQ8Tb.cjs.map +0 -1
  234. package/dist/SNSSubscriber-e7eG48yJ.mjs.map +0 -1
  235. package/dist/SQSConnection-BBlDWZLU.mjs.map +0 -1
  236. package/dist/SQSConnection-DWYDtftQ.cjs.map +0 -1
  237. package/dist/SQSPublisher-B3lS2zbu.mjs.map +0 -1
  238. package/dist/SQSPublisher-nzbSaXSH.cjs.map +0 -1
  239. package/dist/SQSSubscriber-84pFnW1z.mjs.map +0 -1
  240. package/dist/SQSSubscriber-DcL9BbG1.cjs.map +0 -1
@@ -3,146 +3,146 @@ import type { EventConnection } from '../types';
3
3
  import { EventPublisherType } from '../types';
4
4
 
5
5
  export interface RabbitMQConnectionConfig {
6
- url: string;
7
- exchange?: string;
8
- exchangeType?: 'topic' | 'direct' | 'fanout' | 'headers';
9
- exchangeOptions?: amqplib.Options.AssertExchange;
10
- timeout?: number; // Connection timeout in milliseconds (default: 5000)
6
+ url: string;
7
+ exchange?: string;
8
+ exchangeType?: 'topic' | 'direct' | 'fanout' | 'headers';
9
+ exchangeOptions?: amqplib.Options.AssertExchange;
10
+ timeout?: number; // Connection timeout in milliseconds (default: 5000)
11
11
  }
12
12
 
13
13
  export class RabbitMQConnection implements EventConnection {
14
- readonly type = EventPublisherType.RabbitMQ;
15
- private connection?: amqplib.ChannelModel;
16
- private channel?: amqplib.Channel;
17
- private connecting?: Promise<void>;
18
- private config: Required<
19
- Pick<RabbitMQConnectionConfig, 'exchange' | 'exchangeType' | 'timeout'>
20
- > &
21
- RabbitMQConnectionConfig;
22
-
23
- constructor(config: RabbitMQConnectionConfig) {
24
- this.config = {
25
- ...config,
26
- exchange: config.exchange || 'geekmidas.events',
27
- exchangeType: config.exchangeType || 'topic',
28
- timeout: config.timeout ?? 5000,
29
- exchangeOptions: {
30
- durable: true,
31
- ...config.exchangeOptions,
32
- },
33
- };
34
- }
35
-
36
- /**
37
- * Create a RabbitMQConnection from a connection string
38
- * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000
39
- */
40
- static async fromConnectionString(
41
- connectionString: string,
42
- ): Promise<RabbitMQConnection> {
43
- const url = new URL(connectionString);
44
- const params = url.searchParams;
45
-
46
- // Extract connection URL without query params
47
- const baseUrl = `amqp://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;
48
-
49
- const timeoutParam = params.get('timeout');
50
- const config: RabbitMQConnectionConfig = {
51
- url: baseUrl,
52
- exchange: params.get('exchange') || 'geekmidas.events',
53
- exchangeType: (params.get('type') as any) || 'topic',
54
- timeout: timeoutParam ? Number.parseInt(timeoutParam, 10) : undefined,
55
- };
56
-
57
- const connection = new RabbitMQConnection(config);
58
- await connection.connect();
59
- return connection;
60
- }
61
-
62
- async connect(): Promise<void> {
63
- if (this.channel) return;
64
-
65
- // Prevent multiple simultaneous connection attempts
66
- if (this.connecting) {
67
- await this.connecting;
68
- return;
69
- }
70
-
71
- this.connecting = (async () => {
72
- try {
73
- const connection = await amqplib.connect(this.config.url, {
74
- timeout: this.config.timeout,
75
- });
76
- const channel = await connection.createChannel();
77
-
78
- // Assert the exchange exists
79
- await channel.assertExchange(
80
- this.config.exchange!,
81
- this.config.exchangeType!,
82
- this.config.exchangeOptions,
83
- );
84
-
85
- // Handle connection errors
86
- connection.on('error', (_err: Error) => {
87
- this.cleanup();
88
- });
89
-
90
- connection.on('close', () => {
91
- this.cleanup();
92
- });
93
-
94
- // Only set after successful setup
95
- this.connection = connection;
96
- this.channel = channel;
97
- } catch (error) {
98
- this.cleanup();
99
- throw error;
100
- }
101
- })();
102
-
103
- await this.connecting;
104
- this.connecting = undefined;
105
- }
106
-
107
- async close(): Promise<void> {
108
- try {
109
- await this.channel?.close();
110
- await this.connection?.close();
111
- } catch (error) {
112
- // Ignore errors during close
113
- } finally {
114
- this.cleanup();
115
- }
116
- }
117
-
118
- isConnected(): boolean {
119
- return !!this.channel;
120
- }
121
-
122
- private cleanup(): void {
123
- this.channel = undefined;
124
- this.connection = undefined;
125
- this.connecting = undefined;
126
- }
127
-
128
- /**
129
- * Get the underlying channel for publishing/subscribing
130
- */
131
- get amqpChannel(): amqplib.Channel | undefined {
132
- return this.channel;
133
- }
134
-
135
- /**
136
- * Get the exchange name for this connection
137
- */
138
- get exchangeName(): string {
139
- return this.config.exchange;
140
- }
141
-
142
- /**
143
- * Get the exchange type for this connection
144
- */
145
- get exchangeType(): string {
146
- return this.config.exchangeType;
147
- }
14
+ readonly type = EventPublisherType.RabbitMQ;
15
+ private connection?: amqplib.ChannelModel;
16
+ private channel?: amqplib.Channel;
17
+ private connecting?: Promise<void>;
18
+ private config: Required<
19
+ Pick<RabbitMQConnectionConfig, 'exchange' | 'exchangeType' | 'timeout'>
20
+ > &
21
+ RabbitMQConnectionConfig;
22
+
23
+ constructor(config: RabbitMQConnectionConfig) {
24
+ this.config = {
25
+ ...config,
26
+ exchange: config.exchange || 'geekmidas.events',
27
+ exchangeType: config.exchangeType || 'topic',
28
+ timeout: config.timeout ?? 5000,
29
+ exchangeOptions: {
30
+ durable: true,
31
+ ...config.exchangeOptions,
32
+ },
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Create a RabbitMQConnection from a connection string
38
+ * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000
39
+ */
40
+ static async fromConnectionString(
41
+ connectionString: string,
42
+ ): Promise<RabbitMQConnection> {
43
+ const url = new URL(connectionString);
44
+ const params = url.searchParams;
45
+
46
+ // Extract connection URL without query params
47
+ const baseUrl = `amqp://${url.username ? `${url.username}:${url.password}@` : ''}${url.host}${url.pathname}`;
48
+
49
+ const timeoutParam = params.get('timeout');
50
+ const config: RabbitMQConnectionConfig = {
51
+ url: baseUrl,
52
+ exchange: params.get('exchange') || 'geekmidas.events',
53
+ exchangeType: (params.get('type') as any) || 'topic',
54
+ timeout: timeoutParam ? Number.parseInt(timeoutParam, 10) : undefined,
55
+ };
56
+
57
+ const connection = new RabbitMQConnection(config);
58
+ await connection.connect();
59
+ return connection;
60
+ }
61
+
62
+ async connect(): Promise<void> {
63
+ if (this.channel) return;
64
+
65
+ // Prevent multiple simultaneous connection attempts
66
+ if (this.connecting) {
67
+ await this.connecting;
68
+ return;
69
+ }
70
+
71
+ this.connecting = (async () => {
72
+ try {
73
+ const connection = await amqplib.connect(this.config.url, {
74
+ timeout: this.config.timeout,
75
+ });
76
+ const channel = await connection.createChannel();
77
+
78
+ // Assert the exchange exists
79
+ await channel.assertExchange(
80
+ this.config.exchange!,
81
+ this.config.exchangeType!,
82
+ this.config.exchangeOptions,
83
+ );
84
+
85
+ // Handle connection errors
86
+ connection.on('error', (_err: Error) => {
87
+ this.cleanup();
88
+ });
89
+
90
+ connection.on('close', () => {
91
+ this.cleanup();
92
+ });
93
+
94
+ // Only set after successful setup
95
+ this.connection = connection;
96
+ this.channel = channel;
97
+ } catch (error) {
98
+ this.cleanup();
99
+ throw error;
100
+ }
101
+ })();
102
+
103
+ await this.connecting;
104
+ this.connecting = undefined;
105
+ }
106
+
107
+ async close(): Promise<void> {
108
+ try {
109
+ await this.channel?.close();
110
+ await this.connection?.close();
111
+ } catch (_error) {
112
+ // Ignore errors during close
113
+ } finally {
114
+ this.cleanup();
115
+ }
116
+ }
117
+
118
+ isConnected(): boolean {
119
+ return !!this.channel;
120
+ }
121
+
122
+ private cleanup(): void {
123
+ this.channel = undefined;
124
+ this.connection = undefined;
125
+ this.connecting = undefined;
126
+ }
127
+
128
+ /**
129
+ * Get the underlying channel for publishing/subscribing
130
+ */
131
+ get amqpChannel(): amqplib.Channel | undefined {
132
+ return this.channel;
133
+ }
134
+
135
+ /**
136
+ * Get the exchange name for this connection
137
+ */
138
+ get exchangeName(): string {
139
+ return this.config.exchange;
140
+ }
141
+
142
+ /**
143
+ * Get the exchange type for this connection
144
+ */
145
+ get exchangeType(): string {
146
+ return this.config.exchangeType;
147
+ }
148
148
  }
@@ -3,65 +3,65 @@ import type { EventPublisher, PublishableMessage } from '../types';
3
3
  import type { RabbitMQConnection } from './RabbitMQConnection';
4
4
 
5
5
  export interface RabbitMQPublisherOptions {
6
- publishOptions?: amqplib.Options.Publish;
6
+ publishOptions?: amqplib.Options.Publish;
7
7
  }
8
8
 
9
9
  export class RabbitMQPublisher<TMessage extends PublishableMessage<string, any>>
10
- implements EventPublisher<TMessage>
10
+ implements EventPublisher<TMessage>
11
11
  {
12
- constructor(
13
- private connection: RabbitMQConnection,
14
- private options: RabbitMQPublisherOptions = {},
15
- ) {}
12
+ constructor(
13
+ private connection: RabbitMQConnection,
14
+ private options: RabbitMQPublisherOptions = {},
15
+ ) {}
16
16
 
17
- /**
18
- * Create a RabbitMQPublisher from a connection string
19
- * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000
20
- */
21
- static async fromConnectionString<
22
- TMessage extends PublishableMessage<string, any>,
23
- >(connectionString: string): Promise<RabbitMQPublisher<TMessage>> {
24
- const { RabbitMQConnection } = await import('./RabbitMQConnection');
25
- const connection =
26
- await RabbitMQConnection.fromConnectionString(connectionString);
27
- return new RabbitMQPublisher<TMessage>(connection);
28
- }
17
+ /**
18
+ * Create a RabbitMQPublisher from a connection string
19
+ * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&timeout=5000
20
+ */
21
+ static async fromConnectionString<
22
+ TMessage extends PublishableMessage<string, any>,
23
+ >(connectionString: string): Promise<RabbitMQPublisher<TMessage>> {
24
+ const { RabbitMQConnection } = await import('./RabbitMQConnection');
25
+ const connection =
26
+ await RabbitMQConnection.fromConnectionString(connectionString);
27
+ return new RabbitMQPublisher<TMessage>(connection);
28
+ }
29
29
 
30
- async publish(messages: TMessage[]): Promise<void> {
31
- if (!this.connection.isConnected()) {
32
- await this.connection.connect();
33
- }
30
+ async publish(messages: TMessage[]): Promise<void> {
31
+ if (!this.connection.isConnected()) {
32
+ await this.connection.connect();
33
+ }
34
34
 
35
- const channel = this.connection.amqpChannel;
36
- if (!channel) {
37
- throw new Error('Channel not initialized');
38
- }
35
+ const channel = this.connection.amqpChannel;
36
+ if (!channel) {
37
+ throw new Error('Channel not initialized');
38
+ }
39
39
 
40
- const exchange = this.connection.exchangeName;
40
+ const exchange = this.connection.exchangeName;
41
41
 
42
- for (const message of messages) {
43
- const content = Buffer.from(JSON.stringify(message.payload));
42
+ for (const message of messages) {
43
+ const content = Buffer.from(JSON.stringify(message.payload));
44
44
 
45
- // Use message type as routing key for topic exchanges
46
- const routingKey = message.type;
45
+ // Use message type as routing key for topic exchanges
46
+ const routingKey = message.type;
47
47
 
48
- const published = channel.publish(exchange, routingKey, content, {
49
- contentType: 'application/json',
50
- timestamp: Date.now(),
51
- type: message.type,
52
- persistent: true,
53
- ...this.options.publishOptions,
54
- });
48
+ const published = channel.publish(exchange, routingKey, content, {
49
+ contentType: 'application/json',
50
+ timestamp: Date.now(),
51
+ type: message.type,
52
+ persistent: true,
53
+ ...this.options.publishOptions,
54
+ });
55
55
 
56
- // Handle backpressure
57
- if (!published) {
58
- await new Promise((resolve) => channel.once('drain', resolve));
59
- }
60
- }
61
- }
56
+ // Handle backpressure
57
+ if (!published) {
58
+ await new Promise((resolve) => channel.once('drain', resolve));
59
+ }
60
+ }
61
+ }
62
62
 
63
- async close(): Promise<void> {
64
- // Publisher doesn't own the connection
65
- // Connection should be closed by whoever created it
66
- }
63
+ async close(): Promise<void> {
64
+ // Publisher doesn't own the connection
65
+ // Connection should be closed by whoever created it
66
+ }
67
67
  }
@@ -3,111 +3,109 @@ import type { EventSubscriber, PublishableMessage } from '../types';
3
3
  import type { RabbitMQConnection } from './RabbitMQConnection';
4
4
 
5
5
  export interface RabbitMQSubscriberOptions {
6
- queueName?: string; // If not provided, a unique queue will be generated
7
- queueOptions?: amqplib.Options.AssertQueue;
8
- consumeOptions?: amqplib.Options.Consume;
9
- prefetch?: number; // Number of messages to prefetch (default: 1)
6
+ queueName?: string; // If not provided, a unique queue will be generated
7
+ queueOptions?: amqplib.Options.AssertQueue;
8
+ consumeOptions?: amqplib.Options.Consume;
9
+ prefetch?: number; // Number of messages to prefetch (default: 1)
10
10
  }
11
11
 
12
12
  export class RabbitMQSubscriber<
13
- TMessage extends PublishableMessage<string, any>,
13
+ TMessage extends PublishableMessage<string, any>,
14
14
  > implements EventSubscriber<TMessage>
15
15
  {
16
- constructor(
17
- private connection: RabbitMQConnection,
18
- private options: RabbitMQSubscriberOptions = {},
19
- ) {}
20
-
21
- /**
22
- * Create a RabbitMQSubscriber from a connection string
23
- * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&queueName=myqueue&prefetch=10
24
- */
25
- static async fromConnectionString<
26
- TMessage extends PublishableMessage<string, any>,
27
- >(connectionString: string): Promise<RabbitMQSubscriber<TMessage>> {
28
- const url = new URL(connectionString);
29
- const params = url.searchParams;
30
-
31
- const { RabbitMQConnection } = await import('./RabbitMQConnection');
32
- const connection =
33
- await RabbitMQConnection.fromConnectionString(connectionString);
34
-
35
- const options: RabbitMQSubscriberOptions = {
36
- queueName: params.get('queueName') || undefined,
37
- prefetch: params.get('prefetch')
38
- ? Number.parseInt(params.get('prefetch')!, 10)
39
- : undefined,
40
- };
41
-
42
- return new RabbitMQSubscriber<TMessage>(connection, options);
43
- }
44
-
45
- async subscribe(
46
- messages: TMessage['type'][],
47
- listener: (payload: TMessage) => Promise<void>,
48
- ): Promise<void> {
49
- if (!this.connection.isConnected()) {
50
- await this.connection.connect();
51
- }
52
-
53
- const channel = this.connection.amqpChannel;
54
- if (!channel) {
55
- throw new Error('Channel not initialized');
56
- }
57
-
58
- const exchange = this.connection.exchangeName;
59
-
60
- // Set prefetch if specified
61
- if (this.options.prefetch) {
62
- await channel.prefetch(this.options.prefetch);
63
- }
64
-
65
- // Assert queue
66
- const queueName = this.options.queueName || '';
67
- const queueOptions: amqplib.Options.AssertQueue = {
68
- durable: true,
69
- ...this.options.queueOptions,
70
- };
71
-
72
- const { queue } = await channel.assertQueue(queueName, queueOptions);
73
-
74
- // Bind queue to exchange for each message type
75
- for (const messageType of messages) {
76
- await channel.bindQueue(queue, exchange, messageType);
77
- }
78
-
79
- // Consume messages
80
- await channel.consume(
81
- queue,
82
- async (msg) => {
83
- if (!msg) return;
84
-
85
- try {
86
- // Parse message payload
87
- const content = msg.content.toString();
88
- const payload = JSON.parse(content);
89
-
90
- // Get message type from properties
91
- const messageType = msg.properties.type;
92
-
93
- // Reconstruct full message
94
- const fullMessage: TMessage = {
95
- type: messageType,
96
- payload,
97
- } as TMessage;
98
-
99
- // Call listener
100
- await listener(fullMessage);
101
-
102
- // Ack message
103
- channel.ack(msg);
104
- } catch (error) {
105
- // Log error and nack message (requeue)
106
- console.error('Error processing message:', error);
107
- channel.nack(msg, false, true);
108
- }
109
- },
110
- this.options.consumeOptions,
111
- );
112
- }
16
+ constructor(
17
+ private connection: RabbitMQConnection,
18
+ private options: RabbitMQSubscriberOptions = {},
19
+ ) {}
20
+
21
+ /**
22
+ * Create a RabbitMQSubscriber from a connection string
23
+ * Format: rabbitmq://user:pass@host:port/vhost?exchange=name&type=topic&queueName=myqueue&prefetch=10
24
+ */
25
+ static async fromConnectionString<
26
+ TMessage extends PublishableMessage<string, any>,
27
+ >(connectionString: string): Promise<RabbitMQSubscriber<TMessage>> {
28
+ const url = new URL(connectionString);
29
+ const params = url.searchParams;
30
+
31
+ const { RabbitMQConnection } = await import('./RabbitMQConnection');
32
+ const connection =
33
+ await RabbitMQConnection.fromConnectionString(connectionString);
34
+
35
+ const options: RabbitMQSubscriberOptions = {
36
+ queueName: params.get('queueName') || undefined,
37
+ prefetch: params.get('prefetch')
38
+ ? Number.parseInt(params.get('prefetch')!, 10)
39
+ : undefined,
40
+ };
41
+
42
+ return new RabbitMQSubscriber<TMessage>(connection, options);
43
+ }
44
+
45
+ async subscribe(
46
+ messages: TMessage['type'][],
47
+ listener: (payload: TMessage) => Promise<void>,
48
+ ): Promise<void> {
49
+ if (!this.connection.isConnected()) {
50
+ await this.connection.connect();
51
+ }
52
+
53
+ const channel = this.connection.amqpChannel;
54
+ if (!channel) {
55
+ throw new Error('Channel not initialized');
56
+ }
57
+
58
+ const exchange = this.connection.exchangeName;
59
+
60
+ // Set prefetch if specified
61
+ if (this.options.prefetch) {
62
+ await channel.prefetch(this.options.prefetch);
63
+ }
64
+
65
+ // Assert queue
66
+ const queueName = this.options.queueName || '';
67
+ const queueOptions: amqplib.Options.AssertQueue = {
68
+ durable: true,
69
+ ...this.options.queueOptions,
70
+ };
71
+
72
+ const { queue } = await channel.assertQueue(queueName, queueOptions);
73
+
74
+ // Bind queue to exchange for each message type
75
+ for (const messageType of messages) {
76
+ await channel.bindQueue(queue, exchange, messageType);
77
+ }
78
+
79
+ // Consume messages
80
+ await channel.consume(
81
+ queue,
82
+ async (msg) => {
83
+ if (!msg) return;
84
+
85
+ try {
86
+ // Parse message payload
87
+ const content = msg.content.toString();
88
+ const payload = JSON.parse(content);
89
+
90
+ // Get message type from properties
91
+ const messageType = msg.properties.type;
92
+
93
+ // Reconstruct full message
94
+ const fullMessage: TMessage = {
95
+ type: messageType,
96
+ payload,
97
+ } as TMessage;
98
+
99
+ // Call listener
100
+ await listener(fullMessage);
101
+
102
+ // Ack message
103
+ channel.ack(msg);
104
+ } catch (_error) {
105
+ channel.nack(msg, false, true);
106
+ }
107
+ },
108
+ this.options.consumeOptions,
109
+ );
110
+ }
113
111
  }