@geekmidas/events 0.0.2 → 0.2.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 (215) hide show
  1. package/dist/{BasicConnection-C4_Y0nIj.d.mts → BasicConnection-BTRsXiuH.d.cts} +3 -2
  2. package/dist/BasicConnection-BTRsXiuH.d.cts.map +1 -0
  3. package/dist/BasicConnection-C-Cg2FJT.cjs.map +1 -1
  4. package/dist/BasicConnection-CpwybcC9.mjs.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/BasicPublisher-CXAVKxgq.cjs.map +1 -1
  8. package/dist/{BasicPublisher-DCBxaTwC.d.mts → BasicPublisher-CpJFK2oS.d.mts} +5 -3
  9. package/dist/BasicPublisher-CpJFK2oS.d.mts.map +1 -0
  10. package/dist/{BasicPublisher-V5hELHG9.d.cts → BasicPublisher-D1q0a2xp.d.cts} +5 -3
  11. package/dist/BasicPublisher-D1q0a2xp.d.cts.map +1 -0
  12. package/dist/BasicPublisher-DogbeHAH.mjs.map +1 -1
  13. package/dist/{BasicSubscriber-BB3aKeph.d.mts → BasicSubscriber-6w0Af4DD.d.mts} +5 -3
  14. package/dist/BasicSubscriber-6w0Af4DD.d.mts.map +1 -0
  15. package/dist/{BasicSubscriber-CCKfbRMo.mjs → BasicSubscriber-BJSZN22M.mjs} +2 -4
  16. package/dist/BasicSubscriber-BJSZN22M.mjs.map +1 -0
  17. package/dist/{BasicSubscriber-DOwgFw5j.d.cts → BasicSubscriber-DXxQewjc.d.cts} +5 -3
  18. package/dist/BasicSubscriber-DXxQewjc.d.cts.map +1 -0
  19. package/dist/{BasicSubscriber-BGqv3c8x.cjs → BasicSubscriber-mVJRMXhZ.cjs} +2 -4
  20. package/dist/BasicSubscriber-mVJRMXhZ.cjs.map +1 -0
  21. package/dist/{EventConnection-DZhn1Msl.d.cts → EventConnection-BaJRXdg7.d.cts} +2 -2
  22. package/dist/EventConnection-BaJRXdg7.d.cts.map +1 -0
  23. package/dist/EventConnection-ClLMoIUU.mjs.map +1 -1
  24. package/dist/EventConnection-F_N81SXu.cjs.map +1 -1
  25. package/dist/{EventConnection-DeXfLGom.d.mts → EventConnection-SOhtmxib.d.mts} +2 -2
  26. package/dist/EventConnection-SOhtmxib.d.mts.map +1 -0
  27. package/dist/EventConnection.d.cts +2 -2
  28. package/dist/EventConnection.d.mts +2 -2
  29. package/dist/{Publisher-C3m8JFXm.d.mts → Publisher-1CfcYRnm.d.mts} +4 -2
  30. package/dist/Publisher-1CfcYRnm.d.mts.map +1 -0
  31. package/dist/{Publisher-nXNVjL0e.d.cts → Publisher-BfNgvHwQ.d.cts} +4 -2
  32. package/dist/Publisher-BfNgvHwQ.d.cts.map +1 -0
  33. package/dist/Publisher-DS0aOh8r.mjs.map +1 -1
  34. package/dist/Publisher-ceu6OxCj.cjs.map +1 -1
  35. package/dist/Publisher.d.cts +2 -2
  36. package/dist/Publisher.d.mts +2 -2
  37. package/dist/{RabbitMQConnection-ungB0Zl6.cjs → RabbitMQConnection-Blj9Lo05.cjs} +2 -2
  38. package/dist/RabbitMQConnection-Blj9Lo05.cjs.map +1 -0
  39. package/dist/{RabbitMQConnection-7cWp_-_Q.d.cts → RabbitMQConnection-Bm8VCI2R.d.cts} +3 -2
  40. package/dist/RabbitMQConnection-Bm8VCI2R.d.cts.map +1 -0
  41. package/dist/{RabbitMQConnection-C2HC7J7K.d.mts → RabbitMQConnection-Ck-ktCp_.d.mts} +3 -2
  42. package/dist/RabbitMQConnection-Ck-ktCp_.d.mts.map +1 -0
  43. package/dist/{RabbitMQConnection-kU3MvN5P.mjs → RabbitMQConnection-Co7f5Xsx.mjs} +2 -2
  44. package/dist/RabbitMQConnection-Co7f5Xsx.mjs.map +1 -0
  45. package/dist/RabbitMQPublisher--DL6GM6g.cjs.map +1 -1
  46. package/dist/{RabbitMQPublisher-CpcbgrXj.d.cts → RabbitMQPublisher-COnxcWEZ.d.mts} +5 -3
  47. package/dist/RabbitMQPublisher-COnxcWEZ.d.mts.map +1 -0
  48. package/dist/RabbitMQPublisher-o_oVI3wr.mjs.map +1 -1
  49. package/dist/{RabbitMQPublisher-DDZi15Ro.d.mts → RabbitMQPublisher-qQT7-1-p.d.cts} +5 -3
  50. package/dist/RabbitMQPublisher-qQT7-1-p.d.cts.map +1 -0
  51. package/dist/{RabbitMQSubscriber-BplEJEk8.cjs → RabbitMQSubscriber-C8MYAihn.cjs} +2 -3
  52. package/dist/RabbitMQSubscriber-C8MYAihn.cjs.map +1 -0
  53. package/dist/{RabbitMQSubscriber-BJ9PTGny.d.cts → RabbitMQSubscriber-C_sZjDPU.d.mts} +5 -3
  54. package/dist/RabbitMQSubscriber-C_sZjDPU.d.mts.map +1 -0
  55. package/dist/{RabbitMQSubscriber-CIvIw8MG.d.mts → RabbitMQSubscriber-D12sssMb.d.cts} +5 -3
  56. package/dist/RabbitMQSubscriber-D12sssMb.d.cts.map +1 -0
  57. package/dist/{RabbitMQSubscriber-BaXqs5nO.mjs → RabbitMQSubscriber-DlGL0pC2.mjs} +2 -3
  58. package/dist/RabbitMQSubscriber-DlGL0pC2.mjs.map +1 -0
  59. package/dist/{SNSConnection-CAImaMM2.d.mts → SNSConnection-BDfQrVBO.d.cts} +3 -2
  60. package/dist/SNSConnection-BDfQrVBO.d.cts.map +1 -0
  61. package/dist/SNSConnection-D7wCf9Si.mjs.map +1 -1
  62. package/dist/SNSConnection-DVsC_pbB.cjs.map +1 -1
  63. package/dist/{SNSConnection-Cqbr7-Ho.d.cts → SNSConnection-mHb10nQ6.d.mts} +3 -2
  64. package/dist/SNSConnection-mHb10nQ6.d.mts.map +1 -0
  65. package/dist/{SNSPublisher-BZ6N3JI9.cjs → SNSPublisher-BoA8uLnw.cjs} +2 -3
  66. package/dist/SNSPublisher-BoA8uLnw.cjs.map +1 -0
  67. package/dist/{SNSPublisher-Dkhb4LU-.d.mts → SNSPublisher-CM7UAWtf.d.mts} +7 -6
  68. package/dist/SNSPublisher-CM7UAWtf.d.mts.map +1 -0
  69. package/dist/{SNSPublisher-BfWjzNiB.mjs → SNSPublisher-Drr4kHn6.mjs} +2 -3
  70. package/dist/SNSPublisher-Drr4kHn6.mjs.map +1 -0
  71. package/dist/{SNSPublisher-RVZFa7kL.d.cts → SNSPublisher-kpsSlGDn.d.cts} +7 -6
  72. package/dist/SNSPublisher-kpsSlGDn.d.cts.map +1 -0
  73. package/dist/{SNSSubscriber-C9YJWa7m.mjs → SNSSubscriber-CBIvQpqK.mjs} +4 -8
  74. package/dist/SNSSubscriber-CBIvQpqK.mjs.map +1 -0
  75. package/dist/{SNSSubscriber-UE0ia4XM.cjs → SNSSubscriber-CWiAJjpU.cjs} +4 -8
  76. package/dist/SNSSubscriber-CWiAJjpU.cjs.map +1 -0
  77. package/dist/{SNSSubscriber-B31JmkAE.d.mts → SNSSubscriber-DDB3--pm.d.cts} +4 -3
  78. package/dist/SNSSubscriber-DDB3--pm.d.cts.map +1 -0
  79. package/dist/{SNSSubscriber-k2UwKUxu.d.cts → SNSSubscriber-Dwo8391f.d.mts} +4 -3
  80. package/dist/SNSSubscriber-Dwo8391f.d.mts.map +1 -0
  81. package/dist/{SQSConnection-C5WFqZW7.d.mts → SQSConnection-B5Ep9Tma.d.cts} +3 -2
  82. package/dist/SQSConnection-B5Ep9Tma.d.cts.map +1 -0
  83. package/dist/{SQSConnection-geav2Rsn.d.cts → SQSConnection-BI_xOzyJ.d.mts} +3 -2
  84. package/dist/SQSConnection-BI_xOzyJ.d.mts.map +1 -0
  85. package/dist/SQSConnection-BwNjUPr4.cjs.map +1 -1
  86. package/dist/SQSConnection-Eleqqre3.mjs.map +1 -1
  87. package/dist/{SQSPublisher-CdsayOLV.d.mts → SQSPublisher-BN1ds6SN.d.mts} +5 -3
  88. package/dist/SQSPublisher-BN1ds6SN.d.mts.map +1 -0
  89. package/dist/{SQSPublisher-Utb_FTe5.d.cts → SQSPublisher-DFTqwzRW.d.cts} +5 -3
  90. package/dist/SQSPublisher-DFTqwzRW.d.cts.map +1 -0
  91. package/dist/SQSPublisher-DG9o4ME3.mjs.map +1 -1
  92. package/dist/SQSPublisher-Dq5353oZ.cjs.map +1 -1
  93. package/dist/{SQSSubscriber-DZDNgLE5.cjs → SQSSubscriber-B4sRXVl6.cjs} +5 -12
  94. package/dist/SQSSubscriber-B4sRXVl6.cjs.map +1 -0
  95. package/dist/{SQSSubscriber-B6iL1EnX.d.cts → SQSSubscriber-BO2me_gs.d.cts} +4 -3
  96. package/dist/SQSSubscriber-BO2me_gs.d.cts.map +1 -0
  97. package/dist/{SQSSubscriber-CD66P7hH.mjs → SQSSubscriber-CVGhZ8Nr.mjs} +5 -12
  98. package/dist/SQSSubscriber-CVGhZ8Nr.mjs.map +1 -0
  99. package/dist/{SQSSubscriber-DBeE4XAR.d.mts → SQSSubscriber-FyZFfr2-.d.mts} +4 -3
  100. package/dist/SQSSubscriber-FyZFfr2-.d.mts.map +1 -0
  101. package/dist/Subscriber-Bd9hb_jp.mjs.map +1 -1
  102. package/dist/{Subscriber-Cff9EIpv.d.mts → Subscriber-BxT-KzFJ.d.mts} +4 -2
  103. package/dist/Subscriber-BxT-KzFJ.d.mts.map +1 -0
  104. package/dist/{Subscriber-CpAwcUzb.d.cts → Subscriber-DDD_4Nlx.d.cts} +4 -2
  105. package/dist/Subscriber-DDD_4Nlx.d.cts.map +1 -0
  106. package/dist/Subscriber-saVOWuMq.cjs.map +1 -1
  107. package/dist/Subscriber.d.cts +2 -2
  108. package/dist/Subscriber.d.mts +2 -2
  109. package/dist/basic/BasicConnection.d.cts +2 -2
  110. package/dist/basic/BasicConnection.d.mts +2 -2
  111. package/dist/basic/BasicPublisher.d.cts +3 -3
  112. package/dist/basic/BasicPublisher.d.mts +3 -3
  113. package/dist/basic/BasicSubscriber.cjs +1 -1
  114. package/dist/basic/BasicSubscriber.d.cts +3 -3
  115. package/dist/basic/BasicSubscriber.d.mts +3 -3
  116. package/dist/basic/BasicSubscriber.mjs +1 -1
  117. package/dist/basic/index.cjs +1 -1
  118. package/dist/basic/index.d.cts +4 -4
  119. package/dist/basic/index.d.mts +4 -4
  120. package/dist/basic/index.mjs +1 -1
  121. package/dist/index.d.cts +4 -4
  122. package/dist/index.d.mts +4 -4
  123. package/dist/rabbitmq/RabbitMQConnection.cjs +1 -1
  124. package/dist/rabbitmq/RabbitMQConnection.d.cts +2 -2
  125. package/dist/rabbitmq/RabbitMQConnection.d.mts +2 -2
  126. package/dist/rabbitmq/RabbitMQConnection.mjs +1 -1
  127. package/dist/rabbitmq/RabbitMQPublisher.d.cts +3 -3
  128. package/dist/rabbitmq/RabbitMQPublisher.d.mts +3 -3
  129. package/dist/rabbitmq/RabbitMQSubscriber.cjs +1 -1
  130. package/dist/rabbitmq/RabbitMQSubscriber.d.cts +3 -3
  131. package/dist/rabbitmq/RabbitMQSubscriber.d.mts +3 -3
  132. package/dist/rabbitmq/RabbitMQSubscriber.mjs +1 -1
  133. package/dist/rabbitmq/index.cjs +2 -2
  134. package/dist/rabbitmq/index.d.cts +4 -4
  135. package/dist/rabbitmq/index.d.mts +4 -4
  136. package/dist/rabbitmq/index.mjs +2 -2
  137. package/dist/sns/SNSConnection.d.cts +2 -2
  138. package/dist/sns/SNSConnection.d.mts +2 -2
  139. package/dist/sns/SNSPublisher.cjs +1 -1
  140. package/dist/sns/SNSPublisher.d.cts +3 -3
  141. package/dist/sns/SNSPublisher.d.mts +3 -3
  142. package/dist/sns/SNSPublisher.mjs +1 -1
  143. package/dist/sns/SNSSubscriber.cjs +2 -2
  144. package/dist/sns/SNSSubscriber.d.cts +3 -3
  145. package/dist/sns/SNSSubscriber.d.mts +3 -3
  146. package/dist/sns/SNSSubscriber.mjs +2 -2
  147. package/dist/sns/index.cjs +3 -3
  148. package/dist/sns/index.d.cts +4 -4
  149. package/dist/sns/index.d.mts +4 -4
  150. package/dist/sns/index.mjs +3 -3
  151. package/dist/sqs/SQSConnection.d.cts +2 -2
  152. package/dist/sqs/SQSConnection.d.mts +2 -2
  153. package/dist/sqs/SQSPublisher.d.cts +3 -3
  154. package/dist/sqs/SQSPublisher.d.mts +3 -3
  155. package/dist/sqs/SQSSubscriber.cjs +1 -1
  156. package/dist/sqs/SQSSubscriber.d.cts +3 -3
  157. package/dist/sqs/SQSSubscriber.d.mts +3 -3
  158. package/dist/sqs/SQSSubscriber.mjs +1 -1
  159. package/dist/sqs/index.cjs +1 -1
  160. package/dist/sqs/index.d.cts +4 -4
  161. package/dist/sqs/index.d.mts +4 -4
  162. package/dist/sqs/index.mjs +1 -1
  163. package/dist/types-B63qHVOG.mjs.map +1 -1
  164. package/dist/types-CvSMEO5z.cjs.map +1 -1
  165. package/dist/{types-D0g-o6wX.d.mts → types-DSVsPgKd.d.mts} +2 -1
  166. package/dist/types-DSVsPgKd.d.mts.map +1 -0
  167. package/dist/{types-DEGUg5BA.d.cts → types-Dmbp1U-6.d.cts} +2 -1
  168. package/dist/types-Dmbp1U-6.d.cts.map +1 -0
  169. package/dist/types.d.cts +1 -1
  170. package/dist/types.d.mts +1 -1
  171. package/package.json +24 -5
  172. package/src/EventConnection.ts +31 -31
  173. package/src/Publisher.ts +78 -78
  174. package/src/Subscriber.ts +91 -91
  175. package/src/__tests__/EventConnection.spec.ts +42 -0
  176. package/src/__tests__/Publisher.spec.ts +96 -0
  177. package/src/__tests__/Subscriber.spec.ts +91 -23
  178. package/src/basic/BasicConnection.ts +35 -35
  179. package/src/basic/BasicPublisher.ts +8 -8
  180. package/src/basic/BasicSubscriber.ts +13 -16
  181. package/src/basic/__tests__/BasicSubscriber.spec.ts +116 -116
  182. package/src/index.ts +13 -13
  183. package/src/rabbitmq/RabbitMQConnection.ts +139 -139
  184. package/src/rabbitmq/RabbitMQPublisher.ts +48 -48
  185. package/src/rabbitmq/RabbitMQSubscriber.ts +100 -102
  186. package/src/rabbitmq/__tests__/RabbitMQPublisher.spec.ts +364 -364
  187. package/src/rabbitmq/__tests__/RabbitMQSubscriber.spec.ts +204 -204
  188. package/src/rabbitmq/index.ts +3 -3
  189. package/src/sns/SNSConnection.ts +76 -76
  190. package/src/sns/SNSPublisher.ts +54 -56
  191. package/src/sns/SNSSubscriber.ts +187 -191
  192. package/src/sns/__tests__/SNS-SQS-Integration.spec.ts +321 -321
  193. package/src/sns/__tests__/SNSPublisher.spec.ts +449 -0
  194. package/src/sns/__tests__/SNSSubscriber.spec.ts +527 -0
  195. package/src/sns/index.ts +3 -3
  196. package/src/sqs/SQSConnection.ts +75 -75
  197. package/src/sqs/SQSPublisher.ts +93 -93
  198. package/src/sqs/SQSSubscriber.ts +157 -165
  199. package/src/sqs/__tests__/SQSPublisher.spec.ts +178 -178
  200. package/src/sqs/index.ts +3 -3
  201. package/src/types.ts +28 -29
  202. package/tsconfig.json +9 -0
  203. package/tsdown.config.ts +1 -1
  204. package/dist/BasicSubscriber-BGqv3c8x.cjs.map +0 -1
  205. package/dist/BasicSubscriber-CCKfbRMo.mjs.map +0 -1
  206. package/dist/RabbitMQConnection-kU3MvN5P.mjs.map +0 -1
  207. package/dist/RabbitMQConnection-ungB0Zl6.cjs.map +0 -1
  208. package/dist/RabbitMQSubscriber-BaXqs5nO.mjs.map +0 -1
  209. package/dist/RabbitMQSubscriber-BplEJEk8.cjs.map +0 -1
  210. package/dist/SNSPublisher-BZ6N3JI9.cjs.map +0 -1
  211. package/dist/SNSPublisher-BfWjzNiB.mjs.map +0 -1
  212. package/dist/SNSSubscriber-C9YJWa7m.mjs.map +0 -1
  213. package/dist/SNSSubscriber-UE0ia4XM.cjs.map +0 -1
  214. package/dist/SQSSubscriber-CD66P7hH.mjs.map +0 -1
  215. package/dist/SQSSubscriber-DZDNgLE5.cjs.map +0 -1
@@ -0,0 +1,527 @@
1
+ import {
2
+ CreateTopicCommand,
3
+ DeleteTopicCommand,
4
+ SNSClient,
5
+ } from '@aws-sdk/client-sns';
6
+ import {
7
+ GetQueueUrlCommand,
8
+ ListQueuesCommand,
9
+ SQSClient,
10
+ } from '@aws-sdk/client-sqs';
11
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
12
+ import type { PublishableMessage } from '../../types';
13
+ import { SNSConnection } from '../SNSConnection';
14
+ import { SNSPublisher } from '../SNSPublisher';
15
+ import { SNSSubscriber } from '../SNSSubscriber';
16
+
17
+ type TestMessage = PublishableMessage<
18
+ 'user.created' | 'user.updated' | 'order.placed',
19
+ any
20
+ >;
21
+
22
+ const LOCALSTACK_ENDPOINT = 'http://localhost:4566';
23
+ const AWS_REGION = 'us-east-1';
24
+ const AWS_CREDENTIALS = {
25
+ accessKeyId: 'test',
26
+ secretAccessKey: 'test',
27
+ };
28
+
29
+ // Helper functions
30
+ async function createTopic(topicName: string): Promise<string> {
31
+ const client = new SNSClient({
32
+ region: AWS_REGION,
33
+ endpoint: LOCALSTACK_ENDPOINT,
34
+ credentials: AWS_CREDENTIALS,
35
+ });
36
+
37
+ const command = new CreateTopicCommand({ Name: topicName });
38
+ const response = await client.send(command);
39
+ client.destroy();
40
+
41
+ if (!response.TopicArn) {
42
+ throw new Error('Failed to create topic');
43
+ }
44
+
45
+ return response.TopicArn;
46
+ }
47
+
48
+ async function deleteTopic(topicArn: string): Promise<void> {
49
+ const client = new SNSClient({
50
+ region: AWS_REGION,
51
+ endpoint: LOCALSTACK_ENDPOINT,
52
+ credentials: AWS_CREDENTIALS,
53
+ });
54
+
55
+ await client.send(new DeleteTopicCommand({ TopicArn: topicArn }));
56
+ client.destroy();
57
+ }
58
+
59
+ async function queueExists(queueName: string): Promise<boolean> {
60
+ const client = new SQSClient({
61
+ region: AWS_REGION,
62
+ endpoint: LOCALSTACK_ENDPOINT,
63
+ credentials: AWS_CREDENTIALS,
64
+ });
65
+
66
+ try {
67
+ await client.send(new GetQueueUrlCommand({ QueueName: queueName }));
68
+ client.destroy();
69
+ return true;
70
+ } catch {
71
+ client.destroy();
72
+ return false;
73
+ }
74
+ }
75
+
76
+ async function listQueuesWithPrefix(prefix: string): Promise<string[]> {
77
+ const client = new SQSClient({
78
+ region: AWS_REGION,
79
+ endpoint: LOCALSTACK_ENDPOINT,
80
+ credentials: AWS_CREDENTIALS,
81
+ });
82
+
83
+ const response = await client.send(
84
+ new ListQueuesCommand({ QueueNamePrefix: prefix }),
85
+ );
86
+ client.destroy();
87
+
88
+ return response.QueueUrls || [];
89
+ }
90
+
91
+ describe('SNSSubscriber', () => {
92
+ const uniqueId = () =>
93
+ `${Date.now()}-${Math.random().toString(36).substring(7)}`;
94
+
95
+ describe('constructor', () => {
96
+ it('should create subscriber with default options', async () => {
97
+ const topicArn = await createTopic(`test-sub-constructor-${uniqueId()}`);
98
+
99
+ try {
100
+ const connection = new SNSConnection({
101
+ topicArn,
102
+ endpoint: LOCALSTACK_ENDPOINT,
103
+ region: AWS_REGION,
104
+ credentials: AWS_CREDENTIALS,
105
+ });
106
+ await connection.connect();
107
+
108
+ const subscriber = new SNSSubscriber<TestMessage>(connection);
109
+ expect(subscriber).toBeDefined();
110
+
111
+ await connection.close();
112
+ } finally {
113
+ await deleteTopic(topicArn);
114
+ }
115
+ });
116
+
117
+ it('should create subscriber with custom options', async () => {
118
+ const topicArn = await createTopic(`test-sub-options-${uniqueId()}`);
119
+
120
+ try {
121
+ const connection = new SNSConnection({
122
+ topicArn,
123
+ endpoint: LOCALSTACK_ENDPOINT,
124
+ region: AWS_REGION,
125
+ credentials: AWS_CREDENTIALS,
126
+ });
127
+ await connection.connect();
128
+
129
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
130
+ queueName: 'my-custom-queue',
131
+ createQueue: true,
132
+ deleteQueueOnClose: false,
133
+ waitTimeSeconds: 5,
134
+ maxMessages: 5,
135
+ });
136
+
137
+ expect(subscriber).toBeDefined();
138
+
139
+ await connection.close();
140
+ } finally {
141
+ await deleteTopic(topicArn);
142
+ }
143
+ });
144
+ });
145
+
146
+ describe('subscribe and receive messages', () => {
147
+ let topicArn: string;
148
+ let connection: SNSConnection;
149
+ let publisher: SNSPublisher<TestMessage>;
150
+
151
+ beforeAll(async () => {
152
+ topicArn = await createTopic(`test-sub-receive-${uniqueId()}`);
153
+ connection = new SNSConnection({
154
+ topicArn,
155
+ endpoint: LOCALSTACK_ENDPOINT,
156
+ region: AWS_REGION,
157
+ credentials: AWS_CREDENTIALS,
158
+ });
159
+ await connection.connect();
160
+ publisher = new SNSPublisher<TestMessage>(connection);
161
+ });
162
+
163
+ afterAll(async () => {
164
+ await connection.close();
165
+ await deleteTopic(topicArn);
166
+ });
167
+
168
+ it('should receive messages published to SNS topic', async () => {
169
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
170
+ waitTimeSeconds: 1,
171
+ deleteQueueOnClose: true,
172
+ });
173
+
174
+ const receivedMessages: TestMessage[] = [];
175
+
176
+ await subscriber.subscribe(['user.created'], async (message) => {
177
+ receivedMessages.push(message);
178
+ });
179
+
180
+ // Wait for subscription to be ready
181
+ await new Promise((resolve) => setTimeout(resolve, 1000));
182
+
183
+ // Publish message
184
+ await publisher.publish([
185
+ { type: 'user.created', payload: { userId: '123', name: 'Test User' } },
186
+ ]);
187
+
188
+ // Wait for message to be received
189
+ await new Promise((resolve) => setTimeout(resolve, 3000));
190
+
191
+ await subscriber.stop();
192
+
193
+ expect(receivedMessages).toHaveLength(1);
194
+ expect(receivedMessages[0].type).toBe('user.created');
195
+ expect(receivedMessages[0].payload.userId).toBe('123');
196
+ }, 15000);
197
+
198
+ it('should filter messages by type', async () => {
199
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
200
+ waitTimeSeconds: 1,
201
+ deleteQueueOnClose: true,
202
+ });
203
+
204
+ const receivedMessages: TestMessage[] = [];
205
+
206
+ // Only subscribe to user.created
207
+ await subscriber.subscribe(['user.created'], async (message) => {
208
+ receivedMessages.push(message);
209
+ });
210
+
211
+ await new Promise((resolve) => setTimeout(resolve, 1000));
212
+
213
+ // Publish multiple message types
214
+ await publisher.publish([
215
+ { type: 'user.created', payload: { id: '1' } },
216
+ { type: 'user.updated', payload: { id: '2' } },
217
+ { type: 'order.placed', payload: { id: '3' } },
218
+ ]);
219
+
220
+ await new Promise((resolve) => setTimeout(resolve, 3000));
221
+
222
+ await subscriber.stop();
223
+
224
+ // Should only receive user.created
225
+ expect(receivedMessages).toHaveLength(1);
226
+ expect(receivedMessages[0].type).toBe('user.created');
227
+ }, 15000);
228
+
229
+ it('should receive multiple message types when subscribed', async () => {
230
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
231
+ waitTimeSeconds: 1,
232
+ deleteQueueOnClose: true,
233
+ });
234
+
235
+ const receivedMessages: TestMessage[] = [];
236
+
237
+ // Subscribe to multiple types
238
+ await subscriber.subscribe(
239
+ ['user.created', 'order.placed'],
240
+ async (message) => {
241
+ receivedMessages.push(message);
242
+ },
243
+ );
244
+
245
+ await new Promise((resolve) => setTimeout(resolve, 1000));
246
+
247
+ await publisher.publish([
248
+ { type: 'user.created', payload: { id: '1' } },
249
+ { type: 'user.updated', payload: { id: '2' } }, // Not subscribed
250
+ { type: 'order.placed', payload: { id: '3' } },
251
+ ]);
252
+
253
+ await new Promise((resolve) => setTimeout(resolve, 3000));
254
+
255
+ await subscriber.stop();
256
+
257
+ expect(receivedMessages).toHaveLength(2);
258
+ const types = receivedMessages.map((m) => m.type).sort();
259
+ expect(types).toEqual(['order.placed', 'user.created']);
260
+ }, 15000);
261
+ });
262
+
263
+ describe('queue management', () => {
264
+ it('should auto-generate queue name when not provided', async () => {
265
+ const topicArn = await createTopic(`test-auto-queue-${uniqueId()}`);
266
+
267
+ try {
268
+ const connection = new SNSConnection({
269
+ topicArn,
270
+ endpoint: LOCALSTACK_ENDPOINT,
271
+ region: AWS_REGION,
272
+ credentials: AWS_CREDENTIALS,
273
+ });
274
+ await connection.connect();
275
+
276
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
277
+ waitTimeSeconds: 1,
278
+ deleteQueueOnClose: true,
279
+ });
280
+
281
+ await subscriber.subscribe(['user.created'], async () => {});
282
+
283
+ // Check that an auto-generated queue was created
284
+ const queues = await listQueuesWithPrefix('sns-sub-');
285
+ expect(queues.length).toBeGreaterThan(0);
286
+
287
+ await subscriber.stop();
288
+ await connection.close();
289
+ } finally {
290
+ await deleteTopic(topicArn);
291
+ }
292
+ }, 10000);
293
+
294
+ it('should use custom queue name when provided', async () => {
295
+ const topicArn = await createTopic(`test-custom-queue-${uniqueId()}`);
296
+ const customQueueName = `custom-queue-${uniqueId()}`;
297
+
298
+ try {
299
+ const connection = new SNSConnection({
300
+ topicArn,
301
+ endpoint: LOCALSTACK_ENDPOINT,
302
+ region: AWS_REGION,
303
+ credentials: AWS_CREDENTIALS,
304
+ });
305
+ await connection.connect();
306
+
307
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
308
+ queueName: customQueueName,
309
+ waitTimeSeconds: 1,
310
+ deleteQueueOnClose: true,
311
+ });
312
+
313
+ await subscriber.subscribe(['user.created'], async () => {});
314
+
315
+ // Check that the custom queue was created
316
+ const exists = await queueExists(customQueueName);
317
+ expect(exists).toBe(true);
318
+
319
+ await subscriber.stop();
320
+ await connection.close();
321
+ } finally {
322
+ await deleteTopic(topicArn);
323
+ }
324
+ }, 10000);
325
+
326
+ it('should delete auto-generated queue on close', async () => {
327
+ const topicArn = await createTopic(`test-delete-queue-${uniqueId()}`);
328
+ const queuePrefix = `sns-sub-delete-test-${Date.now()}`;
329
+
330
+ try {
331
+ const connection = new SNSConnection({
332
+ topicArn,
333
+ endpoint: LOCALSTACK_ENDPOINT,
334
+ region: AWS_REGION,
335
+ credentials: AWS_CREDENTIALS,
336
+ });
337
+ await connection.connect();
338
+
339
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
340
+ queueName: queuePrefix,
341
+ waitTimeSeconds: 1,
342
+ deleteQueueOnClose: true, // Explicit delete
343
+ });
344
+
345
+ await subscriber.subscribe(['user.created'], async () => {});
346
+
347
+ // Verify queue exists
348
+ expect(await queueExists(queuePrefix)).toBe(true);
349
+
350
+ await subscriber.stop();
351
+
352
+ // Wait for cleanup
353
+ await new Promise((resolve) => setTimeout(resolve, 500));
354
+
355
+ // Verify queue is deleted
356
+ expect(await queueExists(queuePrefix)).toBe(false);
357
+
358
+ await connection.close();
359
+ } finally {
360
+ await deleteTopic(topicArn);
361
+ }
362
+ }, 10000);
363
+
364
+ it('should keep queue when deleteQueueOnClose is false', async () => {
365
+ const topicArn = await createTopic(`test-keep-queue-${uniqueId()}`);
366
+ const customQueueName = `keep-queue-${uniqueId()}`;
367
+
368
+ try {
369
+ const connection = new SNSConnection({
370
+ topicArn,
371
+ endpoint: LOCALSTACK_ENDPOINT,
372
+ region: AWS_REGION,
373
+ credentials: AWS_CREDENTIALS,
374
+ });
375
+ await connection.connect();
376
+
377
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
378
+ queueName: customQueueName,
379
+ waitTimeSeconds: 1,
380
+ deleteQueueOnClose: false, // Don't delete
381
+ });
382
+
383
+ await subscriber.subscribe(['user.created'], async () => {});
384
+
385
+ // Verify queue exists
386
+ expect(await queueExists(customQueueName)).toBe(true);
387
+
388
+ await subscriber.stop();
389
+
390
+ // Wait for cleanup
391
+ await new Promise((resolve) => setTimeout(resolve, 500));
392
+
393
+ // Verify queue still exists
394
+ expect(await queueExists(customQueueName)).toBe(true);
395
+
396
+ await connection.close();
397
+ } finally {
398
+ await deleteTopic(topicArn);
399
+ }
400
+ }, 10000);
401
+ });
402
+
403
+ describe('fromConnectionString', () => {
404
+ it('should create subscriber from connection string', async () => {
405
+ const topicArn = await createTopic(`test-fromcs-sub-${uniqueId()}`);
406
+
407
+ try {
408
+ const connectionString = `sns://?topicArn=${encodeURIComponent(topicArn)}&region=${AWS_REGION}&endpoint=${encodeURIComponent(LOCALSTACK_ENDPOINT)}&accessKeyId=test&secretAccessKey=test&queueName=test-queue-${uniqueId()}`;
409
+
410
+ const subscriber =
411
+ await SNSSubscriber.fromConnectionString<TestMessage>(
412
+ connectionString,
413
+ );
414
+
415
+ expect(subscriber).toBeInstanceOf(SNSSubscriber);
416
+
417
+ await subscriber.stop();
418
+ } finally {
419
+ await deleteTopic(topicArn);
420
+ }
421
+ });
422
+
423
+ it('should parse connection string options', async () => {
424
+ const topicArn = await createTopic(`test-fromcs-opts-${uniqueId()}`);
425
+ const queueName = `parsed-queue-${uniqueId()}`;
426
+
427
+ try {
428
+ const connectionString = `sns://?topicArn=${encodeURIComponent(topicArn)}&region=${AWS_REGION}&endpoint=${encodeURIComponent(LOCALSTACK_ENDPOINT)}&accessKeyId=test&secretAccessKey=test&queueName=${queueName}&waitTimeSeconds=5&deleteQueueOnClose=true`;
429
+
430
+ const subscriber =
431
+ await SNSSubscriber.fromConnectionString<TestMessage>(
432
+ connectionString,
433
+ );
434
+
435
+ // Subscribe to trigger queue creation
436
+ await subscriber.subscribe(['user.created'], async () => {});
437
+
438
+ // Verify queue was created with the specified name
439
+ expect(await queueExists(queueName)).toBe(true);
440
+
441
+ await subscriber.stop();
442
+
443
+ // Queue should be deleted since deleteQueueOnClose=true
444
+ await new Promise((resolve) => setTimeout(resolve, 500));
445
+ expect(await queueExists(queueName)).toBe(false);
446
+ } finally {
447
+ await deleteTopic(topicArn);
448
+ }
449
+ }, 10000);
450
+ });
451
+
452
+ describe('stop', () => {
453
+ it('should stop receiving messages after stop is called', async () => {
454
+ const topicArn = await createTopic(`test-stop-${uniqueId()}`);
455
+
456
+ try {
457
+ const connection = new SNSConnection({
458
+ topicArn,
459
+ endpoint: LOCALSTACK_ENDPOINT,
460
+ region: AWS_REGION,
461
+ credentials: AWS_CREDENTIALS,
462
+ });
463
+ await connection.connect();
464
+
465
+ const publisher = new SNSPublisher<TestMessage>(connection);
466
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
467
+ waitTimeSeconds: 1,
468
+ deleteQueueOnClose: true,
469
+ });
470
+
471
+ const receivedMessages: TestMessage[] = [];
472
+
473
+ await subscriber.subscribe(['user.created'], async (message) => {
474
+ receivedMessages.push(message);
475
+ });
476
+
477
+ await new Promise((resolve) => setTimeout(resolve, 1000));
478
+
479
+ // Publish first message
480
+ await publisher.publish([
481
+ { type: 'user.created', payload: { id: 'before-stop' } },
482
+ ]);
483
+
484
+ await new Promise((resolve) => setTimeout(resolve, 2000));
485
+
486
+ // Stop the subscriber
487
+ await subscriber.stop();
488
+
489
+ // Should have received the first message
490
+ expect(receivedMessages.length).toBeGreaterThanOrEqual(1);
491
+
492
+ await connection.close();
493
+ } finally {
494
+ await deleteTopic(topicArn);
495
+ }
496
+ }, 15000);
497
+
498
+ it('should unsubscribe from SNS topic on stop', async () => {
499
+ const topicArn = await createTopic(`test-unsub-${uniqueId()}`);
500
+
501
+ try {
502
+ const connection = new SNSConnection({
503
+ topicArn,
504
+ endpoint: LOCALSTACK_ENDPOINT,
505
+ region: AWS_REGION,
506
+ credentials: AWS_CREDENTIALS,
507
+ });
508
+ await connection.connect();
509
+
510
+ const subscriber = new SNSSubscriber<TestMessage>(connection, {
511
+ waitTimeSeconds: 1,
512
+ deleteQueueOnClose: true,
513
+ });
514
+
515
+ await subscriber.subscribe(['user.created'], async () => {});
516
+ await new Promise((resolve) => setTimeout(resolve, 500));
517
+
518
+ // Stop should not throw
519
+ await expect(subscriber.stop()).resolves.toBeUndefined();
520
+
521
+ await connection.close();
522
+ } finally {
523
+ await deleteTopic(topicArn);
524
+ }
525
+ }, 10000);
526
+ });
527
+ });
package/src/sns/index.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { SNSConnection } from './SNSConnection';
2
1
  export type { SNSConnectionConfig } from './SNSConnection';
3
- export { SNSPublisher } from './SNSPublisher';
2
+ export { SNSConnection } from './SNSConnection';
4
3
  export type { SNSPublisherOptions } from './SNSPublisher';
5
- export { SNSSubscriber } from './SNSSubscriber';
4
+ export { SNSPublisher } from './SNSPublisher';
6
5
  export type { SNSSubscriberOptions } from './SNSSubscriber';
6
+ export { SNSSubscriber } from './SNSSubscriber';
@@ -3,95 +3,95 @@ import type { EventConnection } from '../types';
3
3
  import { EventPublisherType } from '../types';
4
4
 
5
5
  export interface SQSConnectionConfig {
6
- queueUrl: string;
7
- region?: string;
8
- endpoint?: string; // Custom endpoint (e.g., for LocalStack)
9
- credentials?: {
10
- accessKeyId: string;
11
- secretAccessKey: string;
12
- sessionToken?: string;
13
- };
6
+ queueUrl: string;
7
+ region?: string;
8
+ endpoint?: string; // Custom endpoint (e.g., for LocalStack)
9
+ credentials?: {
10
+ accessKeyId: string;
11
+ secretAccessKey: string;
12
+ sessionToken?: string;
13
+ };
14
14
  }
15
15
 
16
16
  export class SQSConnection implements EventConnection {
17
- readonly type = EventPublisherType.SQS;
18
- private client: SQSClient;
19
- private connected = false;
17
+ readonly type = EventPublisherType.SQS;
18
+ private client: SQSClient;
19
+ private connected = false;
20
20
 
21
- constructor(readonly config: SQSConnectionConfig) {
22
- this.config = config;
21
+ constructor(readonly config: SQSConnectionConfig) {
22
+ this.config = config;
23
23
 
24
- const clientConfig: SQSClientConfig = {
25
- region: config.region,
26
- endpoint: config.endpoint,
27
- credentials: config.credentials,
28
- };
24
+ const clientConfig: SQSClientConfig = {
25
+ region: config.region,
26
+ endpoint: config.endpoint,
27
+ credentials: config.credentials,
28
+ };
29
29
 
30
- this.client = new SQSClient(clientConfig);
31
- }
30
+ this.client = new SQSClient(clientConfig);
31
+ }
32
32
 
33
- /**
34
- * Create an SQSConnection from a connection string
35
- * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566
36
- */
37
- static async fromConnectionString(
38
- connectionString: string,
39
- ): Promise<SQSConnection> {
40
- const url = new URL(connectionString);
41
- const params = url.searchParams;
33
+ /**
34
+ * Create an SQSConnection from a connection string
35
+ * Format: sqs://?queueUrl=https://sqs.region.amazonaws.com/accountId/queueName&region=us-east-1&endpoint=http://localhost:4566
36
+ */
37
+ static async fromConnectionString(
38
+ connectionString: string,
39
+ ): Promise<SQSConnection> {
40
+ const url = new URL(connectionString);
41
+ const params = url.searchParams;
42
42
 
43
- const queueUrl = params.get('queueUrl');
44
- if (!queueUrl) {
45
- throw new Error('queueUrl parameter is required in connection string');
46
- }
43
+ const queueUrl = params.get('queueUrl');
44
+ if (!queueUrl) {
45
+ throw new Error('queueUrl parameter is required in connection string');
46
+ }
47
47
 
48
- const config: SQSConnectionConfig = {
49
- queueUrl,
50
- region: params.get('region') || undefined,
51
- endpoint: params.get('endpoint') || undefined,
52
- };
48
+ const config: SQSConnectionConfig = {
49
+ queueUrl,
50
+ region: params.get('region') || undefined,
51
+ endpoint: params.get('endpoint') || undefined,
52
+ };
53
53
 
54
- // Parse credentials if provided
55
- const accessKeyId = params.get('accessKeyId');
56
- const secretAccessKey = params.get('secretAccessKey');
57
- if (accessKeyId && secretAccessKey) {
58
- config.credentials = {
59
- accessKeyId,
60
- secretAccessKey,
61
- sessionToken: params.get('sessionToken') || undefined,
62
- };
63
- }
54
+ // Parse credentials if provided
55
+ const accessKeyId = params.get('accessKeyId');
56
+ const secretAccessKey = params.get('secretAccessKey');
57
+ if (accessKeyId && secretAccessKey) {
58
+ config.credentials = {
59
+ accessKeyId,
60
+ secretAccessKey,
61
+ sessionToken: params.get('sessionToken') || undefined,
62
+ };
63
+ }
64
64
 
65
- const connection = new SQSConnection(config);
66
- await connection.connect();
67
- return connection;
68
- }
65
+ const connection = new SQSConnection(config);
66
+ await connection.connect();
67
+ return connection;
68
+ }
69
69
 
70
- async connect(): Promise<void> {
71
- // SQS doesn't have a traditional connection, but we mark as connected
72
- this.connected = true;
73
- }
70
+ async connect(): Promise<void> {
71
+ // SQS doesn't have a traditional connection, but we mark as connected
72
+ this.connected = true;
73
+ }
74
74
 
75
- async close(): Promise<void> {
76
- this.client.destroy();
77
- this.connected = false;
78
- }
75
+ async close(): Promise<void> {
76
+ this.client.destroy();
77
+ this.connected = false;
78
+ }
79
79
 
80
- isConnected(): boolean {
81
- return this.connected;
82
- }
80
+ isConnected(): boolean {
81
+ return this.connected;
82
+ }
83
83
 
84
- /**
85
- * Get the underlying SQS client
86
- */
87
- get sqsClient(): SQSClient {
88
- return this.client;
89
- }
84
+ /**
85
+ * Get the underlying SQS client
86
+ */
87
+ get sqsClient(): SQSClient {
88
+ return this.client;
89
+ }
90
90
 
91
- /**
92
- * Get the queue URL for this connection
93
- */
94
- get queueUrl(): string {
95
- return this.config.queueUrl;
96
- }
91
+ /**
92
+ * Get the queue URL for this connection
93
+ */
94
+ get queueUrl(): string {
95
+ return this.config.queueUrl;
96
+ }
97
97
  }