@geekmidas/events 0.0.1

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 (211) hide show
  1. package/README.md +473 -0
  2. package/dist/BasicConnection-BNi6_olg.mjs +42 -0
  3. package/dist/BasicConnection-BNi6_olg.mjs.map +1 -0
  4. package/dist/BasicConnection-C4_Y0nIj.d.mts +26 -0
  5. package/dist/BasicConnection-CddnULuT.d.cts +26 -0
  6. package/dist/BasicConnection-CzfMubv_.cjs +48 -0
  7. package/dist/BasicConnection-CzfMubv_.cjs.map +1 -0
  8. package/dist/BasicPublisher-BzfzHIfb.cjs +20 -0
  9. package/dist/BasicPublisher-BzfzHIfb.cjs.map +1 -0
  10. package/dist/BasicPublisher-Cg3SxIVR.mjs +14 -0
  11. package/dist/BasicPublisher-Cg3SxIVR.mjs.map +1 -0
  12. package/dist/BasicPublisher-DCBxaTwC.d.mts +12 -0
  13. package/dist/BasicPublisher-V5hELHG9.d.cts +12 -0
  14. package/dist/BasicSubscriber-BB3aKeph.d.mts +12 -0
  15. package/dist/BasicSubscriber-BEv5N21f.mjs +18 -0
  16. package/dist/BasicSubscriber-BEv5N21f.mjs.map +1 -0
  17. package/dist/BasicSubscriber-DOwgFw5j.d.cts +12 -0
  18. package/dist/BasicSubscriber-DyTKOixe.cjs +24 -0
  19. package/dist/BasicSubscriber-DyTKOixe.cjs.map +1 -0
  20. package/dist/EventConnection-ClLMoIUU.mjs +36 -0
  21. package/dist/EventConnection-ClLMoIUU.mjs.map +1 -0
  22. package/dist/EventConnection-DZhn1Msl.d.cts +13 -0
  23. package/dist/EventConnection-DeXfLGom.d.mts +13 -0
  24. package/dist/EventConnection-F_N81SXu.cjs +41 -0
  25. package/dist/EventConnection-F_N81SXu.cjs.map +1 -0
  26. package/dist/EventConnection.cjs +5 -0
  27. package/dist/EventConnection.d.cts +3 -0
  28. package/dist/EventConnection.d.mts +3 -0
  29. package/dist/EventConnection.mjs +4 -0
  30. package/dist/Publisher-C3m8JFXm.d.mts +19 -0
  31. package/dist/Publisher-DS0aOh8r.mjs +65 -0
  32. package/dist/Publisher-DS0aOh8r.mjs.map +1 -0
  33. package/dist/Publisher-ceu6OxCj.cjs +70 -0
  34. package/dist/Publisher-ceu6OxCj.cjs.map +1 -0
  35. package/dist/Publisher-nXNVjL0e.d.cts +19 -0
  36. package/dist/Publisher.cjs +4 -0
  37. package/dist/Publisher.d.cts +3 -0
  38. package/dist/Publisher.d.mts +3 -0
  39. package/dist/Publisher.mjs +4 -0
  40. package/dist/RabbitMQConnection-7cWp_-_Q.d.cts +43 -0
  41. package/dist/RabbitMQConnection-BN_GybOv.cjs +113 -0
  42. package/dist/RabbitMQConnection-BN_GybOv.cjs.map +1 -0
  43. package/dist/RabbitMQConnection-C2HC7J7K.d.mts +43 -0
  44. package/dist/RabbitMQConnection-eVOVS4Dl.mjs +107 -0
  45. package/dist/RabbitMQConnection-eVOVS4Dl.mjs.map +1 -0
  46. package/dist/RabbitMQPublisher-BZX0edgL.mjs +39 -0
  47. package/dist/RabbitMQPublisher-BZX0edgL.mjs.map +1 -0
  48. package/dist/RabbitMQPublisher-CSCn6gMA.cjs +45 -0
  49. package/dist/RabbitMQPublisher-CSCn6gMA.cjs.map +1 -0
  50. package/dist/RabbitMQPublisher-CpcbgrXj.d.cts +23 -0
  51. package/dist/RabbitMQPublisher-DDZi15Ro.d.mts +23 -0
  52. package/dist/RabbitMQSubscriber-B5I-hBaT.mjs +57 -0
  53. package/dist/RabbitMQSubscriber-B5I-hBaT.mjs.map +1 -0
  54. package/dist/RabbitMQSubscriber-BJ9PTGny.d.cts +25 -0
  55. package/dist/RabbitMQSubscriber-CIvIw8MG.d.mts +25 -0
  56. package/dist/RabbitMQSubscriber-Cj7qLIi6.cjs +63 -0
  57. package/dist/RabbitMQSubscriber-Cj7qLIi6.cjs.map +1 -0
  58. package/dist/SNSConnection-CAImaMM2.d.mts +40 -0
  59. package/dist/SNSConnection-Cqbr7-Ho.d.cts +40 -0
  60. package/dist/SNSConnection-D7wCf9Si.mjs +70 -0
  61. package/dist/SNSConnection-D7wCf9Si.mjs.map +1 -0
  62. package/dist/SNSConnection-DVsC_pbB.cjs +76 -0
  63. package/dist/SNSConnection-DVsC_pbB.cjs.map +1 -0
  64. package/dist/SNSPublisher-BZ6N3JI9.cjs +48 -0
  65. package/dist/SNSPublisher-BZ6N3JI9.cjs.map +1 -0
  66. package/dist/SNSPublisher-BfWjzNiB.mjs +42 -0
  67. package/dist/SNSPublisher-BfWjzNiB.mjs.map +1 -0
  68. package/dist/SNSPublisher-Dkhb4LU-.d.mts +21 -0
  69. package/dist/SNSPublisher-RVZFa7kL.d.cts +21 -0
  70. package/dist/SNSSubscriber-B31JmkAE.d.mts +54 -0
  71. package/dist/SNSSubscriber-C9YJWa7m.mjs +143 -0
  72. package/dist/SNSSubscriber-C9YJWa7m.mjs.map +1 -0
  73. package/dist/SNSSubscriber-UE0ia4XM.cjs +149 -0
  74. package/dist/SNSSubscriber-UE0ia4XM.cjs.map +1 -0
  75. package/dist/SNSSubscriber-k2UwKUxu.d.cts +54 -0
  76. package/dist/SQSConnection-BwNjUPr4.cjs +76 -0
  77. package/dist/SQSConnection-BwNjUPr4.cjs.map +1 -0
  78. package/dist/SQSConnection-C5WFqZW7.d.mts +40 -0
  79. package/dist/SQSConnection-Eleqqre3.mjs +70 -0
  80. package/dist/SQSConnection-Eleqqre3.mjs.map +1 -0
  81. package/dist/SQSConnection-geav2Rsn.d.cts +40 -0
  82. package/dist/SQSPublisher-CdsayOLV.d.mts +23 -0
  83. package/dist/SQSPublisher-DG9o4ME3.mjs +57 -0
  84. package/dist/SQSPublisher-DG9o4ME3.mjs.map +1 -0
  85. package/dist/SQSPublisher-Dq5353oZ.cjs +63 -0
  86. package/dist/SQSPublisher-Dq5353oZ.cjs.map +1 -0
  87. package/dist/SQSPublisher-Utb_FTe5.d.cts +23 -0
  88. package/dist/SQSSubscriber-B6iL1EnX.d.cts +43 -0
  89. package/dist/SQSSubscriber-CD66P7hH.mjs +121 -0
  90. package/dist/SQSSubscriber-CD66P7hH.mjs.map +1 -0
  91. package/dist/SQSSubscriber-DBeE4XAR.d.mts +43 -0
  92. package/dist/SQSSubscriber-DZDNgLE5.cjs +127 -0
  93. package/dist/SQSSubscriber-DZDNgLE5.cjs.map +1 -0
  94. package/dist/Subscriber-Bd9hb_jp.mjs +74 -0
  95. package/dist/Subscriber-Bd9hb_jp.mjs.map +1 -0
  96. package/dist/Subscriber-Cff9EIpv.d.mts +19 -0
  97. package/dist/Subscriber-CpAwcUzb.d.cts +19 -0
  98. package/dist/Subscriber-saVOWuMq.cjs +79 -0
  99. package/dist/Subscriber-saVOWuMq.cjs.map +1 -0
  100. package/dist/Subscriber.cjs +4 -0
  101. package/dist/Subscriber.d.cts +3 -0
  102. package/dist/Subscriber.d.mts +3 -0
  103. package/dist/Subscriber.mjs +4 -0
  104. package/dist/basic/BasicConnection.cjs +4 -0
  105. package/dist/basic/BasicConnection.d.cts +3 -0
  106. package/dist/basic/BasicConnection.d.mts +3 -0
  107. package/dist/basic/BasicConnection.mjs +4 -0
  108. package/dist/basic/BasicPublisher.cjs +3 -0
  109. package/dist/basic/BasicPublisher.d.cts +4 -0
  110. package/dist/basic/BasicPublisher.d.mts +4 -0
  111. package/dist/basic/BasicPublisher.mjs +3 -0
  112. package/dist/basic/BasicSubscriber.cjs +3 -0
  113. package/dist/basic/BasicSubscriber.d.cts +4 -0
  114. package/dist/basic/BasicSubscriber.d.mts +4 -0
  115. package/dist/basic/BasicSubscriber.mjs +3 -0
  116. package/dist/basic/index.cjs +8 -0
  117. package/dist/basic/index.d.cts +5 -0
  118. package/dist/basic/index.d.mts +5 -0
  119. package/dist/basic/index.mjs +6 -0
  120. package/dist/chunk-CUT6urMc.cjs +30 -0
  121. package/dist/index.cjs +9 -0
  122. package/dist/index.d.cts +5 -0
  123. package/dist/index.d.mts +5 -0
  124. package/dist/index.mjs +6 -0
  125. package/dist/rabbitmq/RabbitMQConnection.cjs +4 -0
  126. package/dist/rabbitmq/RabbitMQConnection.d.cts +3 -0
  127. package/dist/rabbitmq/RabbitMQConnection.d.mts +3 -0
  128. package/dist/rabbitmq/RabbitMQConnection.mjs +4 -0
  129. package/dist/rabbitmq/RabbitMQPublisher.cjs +3 -0
  130. package/dist/rabbitmq/RabbitMQPublisher.d.cts +4 -0
  131. package/dist/rabbitmq/RabbitMQPublisher.d.mts +4 -0
  132. package/dist/rabbitmq/RabbitMQPublisher.mjs +3 -0
  133. package/dist/rabbitmq/RabbitMQSubscriber.cjs +3 -0
  134. package/dist/rabbitmq/RabbitMQSubscriber.d.cts +4 -0
  135. package/dist/rabbitmq/RabbitMQSubscriber.d.mts +4 -0
  136. package/dist/rabbitmq/RabbitMQSubscriber.mjs +3 -0
  137. package/dist/rabbitmq/index.cjs +8 -0
  138. package/dist/rabbitmq/index.d.cts +5 -0
  139. package/dist/rabbitmq/index.d.mts +5 -0
  140. package/dist/rabbitmq/index.mjs +6 -0
  141. package/dist/sns/SNSConnection.cjs +4 -0
  142. package/dist/sns/SNSConnection.d.cts +3 -0
  143. package/dist/sns/SNSConnection.d.mts +3 -0
  144. package/dist/sns/SNSConnection.mjs +4 -0
  145. package/dist/sns/SNSPublisher.cjs +3 -0
  146. package/dist/sns/SNSPublisher.d.cts +4 -0
  147. package/dist/sns/SNSPublisher.d.mts +4 -0
  148. package/dist/sns/SNSPublisher.mjs +3 -0
  149. package/dist/sns/SNSSubscriber.cjs +6 -0
  150. package/dist/sns/SNSSubscriber.d.cts +4 -0
  151. package/dist/sns/SNSSubscriber.d.mts +4 -0
  152. package/dist/sns/SNSSubscriber.mjs +6 -0
  153. package/dist/sns/index.cjs +10 -0
  154. package/dist/sns/index.d.cts +5 -0
  155. package/dist/sns/index.d.mts +5 -0
  156. package/dist/sns/index.mjs +8 -0
  157. package/dist/sqs/SQSConnection.cjs +4 -0
  158. package/dist/sqs/SQSConnection.d.cts +3 -0
  159. package/dist/sqs/SQSConnection.d.mts +3 -0
  160. package/dist/sqs/SQSConnection.mjs +4 -0
  161. package/dist/sqs/SQSPublisher.cjs +3 -0
  162. package/dist/sqs/SQSPublisher.d.cts +4 -0
  163. package/dist/sqs/SQSPublisher.d.mts +4 -0
  164. package/dist/sqs/SQSPublisher.mjs +3 -0
  165. package/dist/sqs/SQSSubscriber.cjs +3 -0
  166. package/dist/sqs/SQSSubscriber.d.cts +4 -0
  167. package/dist/sqs/SQSSubscriber.d.mts +4 -0
  168. package/dist/sqs/SQSSubscriber.mjs +3 -0
  169. package/dist/sqs/index.cjs +8 -0
  170. package/dist/sqs/index.d.cts +5 -0
  171. package/dist/sqs/index.d.mts +5 -0
  172. package/dist/sqs/index.mjs +6 -0
  173. package/dist/types-B63qHVOG.mjs +17 -0
  174. package/dist/types-B63qHVOG.mjs.map +1 -0
  175. package/dist/types-CvSMEO5z.cjs +23 -0
  176. package/dist/types-CvSMEO5z.cjs.map +1 -0
  177. package/dist/types-D0g-o6wX.d.mts +65 -0
  178. package/dist/types-DEGUg5BA.d.cts +65 -0
  179. package/dist/types.cjs +3 -0
  180. package/dist/types.d.cts +2 -0
  181. package/dist/types.d.mts +2 -0
  182. package/dist/types.mjs +3 -0
  183. package/package.json +49 -0
  184. package/src/EventConnection.ts +39 -0
  185. package/src/Publisher.ts +87 -0
  186. package/src/Subscriber.ts +102 -0
  187. package/src/__tests__/Subscriber.spec.ts +41 -0
  188. package/src/basic/BasicConnection.ts +47 -0
  189. package/src/basic/BasicPublisher.ts +15 -0
  190. package/src/basic/BasicSubscriber.ts +24 -0
  191. package/src/basic/__tests__/BasicSubscriber.spec.ts +166 -0
  192. package/src/basic/index.ts +3 -0
  193. package/src/index.ts +25 -0
  194. package/src/rabbitmq/RabbitMQConnection.ts +148 -0
  195. package/src/rabbitmq/RabbitMQPublisher.ts +67 -0
  196. package/src/rabbitmq/RabbitMQSubscriber.ts +113 -0
  197. package/src/rabbitmq/__tests__/RabbitMQPublisher.spec.ts +378 -0
  198. package/src/rabbitmq/__tests__/RabbitMQSubscriber.spec.ts +292 -0
  199. package/src/rabbitmq/index.ts +6 -0
  200. package/src/sns/SNSConnection.ts +98 -0
  201. package/src/sns/SNSPublisher.ts +64 -0
  202. package/src/sns/SNSSubscriber.ts +222 -0
  203. package/src/sns/__tests__/SNS-SQS-Integration.spec.ts +389 -0
  204. package/src/sns/index.ts +6 -0
  205. package/src/sqs/SQSConnection.ts +97 -0
  206. package/src/sqs/SQSPublisher.ts +105 -0
  207. package/src/sqs/SQSSubscriber.ts +189 -0
  208. package/src/sqs/__tests__/SQSPublisher.spec.ts +189 -0
  209. package/src/sqs/index.ts +6 -0
  210. package/src/types.ts +76 -0
  211. package/tsdown.config.ts +5 -0
package/README.md ADDED
@@ -0,0 +1,473 @@
1
+ # @geekmidas/events
2
+
3
+ A unified event messaging library with support for multiple backends (Basic, RabbitMQ, SQS, SNS). Write your event-driven code once and switch between messaging systems without changing your application logic.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Unified Interface**: Single API for publishing and subscribing to events across different messaging systems
8
+ - ✅ **Type-Safe**: Full TypeScript support with type inference for message types and payloads
9
+ - ✅ **Multiple Backends**: Basic (in-memory), RabbitMQ, AWS SQS, AWS SNS
10
+ - ✅ **Connection Strings**: Configure messaging systems using simple connection strings
11
+ - ✅ **Factory Pattern**: Create publishers and subscribers from connections or connection strings
12
+ - ✅ **SNS→SQS Integration**: Managed push-based messaging with automatic queue setup
13
+ - ✅ **Flexible**: Direct SQS access or managed SNS subscription patterns
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pnpm add @geekmidas/events
19
+ ```
20
+
21
+ ### Peer Dependencies
22
+
23
+ Depending on which messaging backend you use, install the appropriate SDK:
24
+
25
+ ```bash
26
+ # For RabbitMQ
27
+ pnpm add amqplib
28
+
29
+ # For AWS SQS/SNS
30
+ pnpm add @aws-sdk/client-sqs @aws-sdk/client-sns
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ### 1. Define Your Message Types
36
+
37
+ ```typescript
38
+ import type { PublishableMessage } from '@geekmidas/events';
39
+
40
+ type UserEvents =
41
+ | PublishableMessage<'user.created', { userId: string; email: string }>
42
+ | PublishableMessage<'user.updated', { userId: string; changes: Record<string, any> }>
43
+ | PublishableMessage<'user.deleted', { userId: string }>;
44
+ ```
45
+
46
+ ### 2. Create Publisher and Subscriber
47
+
48
+ ```typescript
49
+ import { Publisher, Subscriber } from '@geekmidas/events';
50
+
51
+ // Create publisher from connection string
52
+ const publisher = await Publisher.fromConnectionString<UserEvents>(
53
+ 'rabbitmq://localhost:5672?exchange=events'
54
+ );
55
+
56
+ // Create subscriber from connection string
57
+ const subscriber = await Subscriber.fromConnectionString<UserEvents>(
58
+ 'rabbitmq://localhost:5672?exchange=events&queue=user-service'
59
+ );
60
+
61
+ // Subscribe to events
62
+ await subscriber.subscribe(['user.created', 'user.updated'], async (message) => {
63
+ console.log('Received:', message.type, message.payload);
64
+ });
65
+
66
+ // Publish events
67
+ await publisher.publish([
68
+ {
69
+ type: 'user.created',
70
+ payload: { userId: '123', email: 'user@example.com' }
71
+ }
72
+ ]);
73
+ ```
74
+
75
+ ## Messaging Backends
76
+
77
+ ### Basic (In-Memory)
78
+
79
+ Perfect for testing and development. Uses Node.js EventEmitter.
80
+
81
+ ```typescript
82
+ import { BasicConnection, BasicPublisher, BasicSubscriber } from '@geekmidas/events/basic';
83
+
84
+ const connection = new BasicConnection();
85
+ await connection.connect();
86
+
87
+ const publisher = new BasicPublisher(connection);
88
+ const subscriber = new BasicSubscriber(connection);
89
+
90
+ await subscriber.subscribe(['user.created'], async (message) => {
91
+ console.log('User created:', message.payload);
92
+ });
93
+
94
+ await publisher.publish([
95
+ { type: 'user.created', payload: { userId: '123' } }
96
+ ]);
97
+ ```
98
+
99
+ **Connection String Format:**
100
+ ```
101
+ basic://
102
+ ```
103
+
104
+ ### RabbitMQ
105
+
106
+ Production-ready message broker with advanced features.
107
+
108
+ ```typescript
109
+ import { RabbitMQConnection, RabbitMQPublisher, RabbitMQSubscriber } from '@geekmidas/events/rabbitmq';
110
+
111
+ const connection = new RabbitMQConnection({
112
+ url: 'amqp://localhost:5672',
113
+ exchange: 'events',
114
+ exchangeType: 'topic',
115
+ });
116
+ await connection.connect();
117
+
118
+ const publisher = new RabbitMQPublisher(connection);
119
+ const subscriber = new RabbitMQSubscriber(connection, {
120
+ queueName: 'user-service',
121
+ prefetch: 10,
122
+ });
123
+
124
+ await subscriber.subscribe(['user.created', 'user.updated'], async (message) => {
125
+ console.log('Processing:', message.type, message.payload);
126
+ });
127
+
128
+ await publisher.publish([
129
+ { type: 'user.created', payload: { userId: '123' } }
130
+ ]);
131
+ ```
132
+
133
+ **Connection String Format:**
134
+ ```
135
+ rabbitmq://localhost:5672?exchange=events&exchangeType=topic&queue=myqueue
136
+ ```
137
+
138
+ **Options:**
139
+ - `exchange` - Exchange name (required)
140
+ - `exchangeType` - Exchange type: `topic`, `fanout`, `direct` (default: `topic`)
141
+ - `queue` - Queue name (optional, auto-generated if not provided)
142
+ - `prefetch` - Prefetch limit (default: 10)
143
+
144
+ ### AWS SQS
145
+
146
+ Direct SQS queue access for pull-based messaging.
147
+
148
+ ```typescript
149
+ import { SQSConnection, SQSPublisher, SQSSubscriber } from '@geekmidas/events/sqs';
150
+
151
+ const connection = new SQSConnection({
152
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
153
+ region: 'us-east-1',
154
+ });
155
+ await connection.connect();
156
+
157
+ const publisher = new SQSPublisher(connection);
158
+ const subscriber = new SQSSubscriber(connection, {
159
+ waitTimeSeconds: 20, // Long polling
160
+ maxMessages: 10,
161
+ });
162
+
163
+ await subscriber.subscribe(['user.created'], async (message) => {
164
+ console.log('Received from SQS:', message.payload);
165
+ });
166
+
167
+ await publisher.publish([
168
+ { type: 'user.created', payload: { userId: '123' } }
169
+ ]);
170
+ ```
171
+
172
+ **Connection String Format:**
173
+ ```
174
+ sqs://?queueUrl=https://sqs.us-east-1.amazonaws.com/123456789/my-queue&region=us-east-1
175
+ ```
176
+
177
+ **Options:**
178
+ - `queueUrl` - SQS queue URL (required)
179
+ - `region` - AWS region (required)
180
+ - `endpoint` - Custom endpoint for LocalStack (optional)
181
+ - `accessKeyId`, `secretAccessKey` - AWS credentials (optional)
182
+ - `waitTimeSeconds` - Long polling duration (default: 20)
183
+ - `maxMessages` - Max messages per poll (default: 10)
184
+
185
+ ### AWS SNS
186
+
187
+ Push-based pub/sub messaging with automatic SQS queue management.
188
+
189
+ ```typescript
190
+ import { SNSConnection, SNSPublisher, SNSSubscriber } from '@geekmidas/events/sns';
191
+
192
+ const connection = new SNSConnection({
193
+ topicArn: 'arn:aws:sns:us-east-1:123456789:events',
194
+ region: 'us-east-1',
195
+ });
196
+ await connection.connect();
197
+
198
+ const publisher = new SNSPublisher(connection);
199
+
200
+ // SNS Subscriber automatically creates and manages an SQS queue
201
+ const subscriber = new SNSSubscriber(connection, {
202
+ queueName: 'my-service-queue', // Optional
203
+ createQueue: true,
204
+ deleteQueueOnClose: false,
205
+ });
206
+
207
+ await subscriber.subscribe(['user.created'], async (message) => {
208
+ console.log('Received via SNS:', message.payload);
209
+ });
210
+
211
+ await publisher.publish([
212
+ { type: 'user.created', payload: { userId: '123' } }
213
+ ]);
214
+
215
+ // Cleanup
216
+ await subscriber.stop(); // Unsubscribes and optionally deletes queue
217
+ ```
218
+
219
+ **Connection String Format:**
220
+ ```
221
+ sns://?topicArn=arn:aws:sns:us-east-1:123456789:events&region=us-east-1&queueName=my-queue
222
+ ```
223
+
224
+ **Options:**
225
+ - `topicArn` - SNS topic ARN (required)
226
+ - `region` - AWS region (required)
227
+ - `endpoint` - Custom endpoint for LocalStack (optional)
228
+ - `queueName` - SQS queue name (optional, auto-generated if not provided)
229
+ - `createQueue` - Auto-create queue (default: true)
230
+ - `deleteQueueOnClose` - Delete queue on stop (default: true for auto-generated queues)
231
+
232
+ ## Advanced Usage
233
+
234
+ ### Managed SNS→SQS Subscription via SQS Connection String
235
+
236
+ You can use a SQS connection string with a `topicArn` parameter to automatically get managed SNS subscription:
237
+
238
+ ```typescript
239
+ import { Subscriber } from '@geekmidas/events';
240
+
241
+ // This automatically creates an SNS subscriber with managed queue
242
+ const subscriber = await Subscriber.fromConnectionString(
243
+ 'sqs://?topicArn=arn:aws:sns:us-east-1:123456789:events&queueName=my-queue&region=us-east-1'
244
+ );
245
+ ```
246
+
247
+ When `topicArn` is present in an SQS connection string, it automatically:
248
+ 1. Creates an SNS connection
249
+ 2. Sets up managed SQS queue
250
+ 3. Subscribes queue to SNS topic
251
+ 4. Filters messages by topic ARN and message type
252
+
253
+ ### Manual SNS→SQS Integration
254
+
255
+ For more control, manually subscribe an SQS queue to an SNS topic:
256
+
257
+ ```typescript
258
+ import { SNSPublisher, SNSConnection } from '@geekmidas/events/sns';
259
+ import { SQSSubscriber, SQSConnection } from '@geekmidas/events/sqs';
260
+
261
+ // Publisher sends to SNS
262
+ const snsConnection = new SNSConnection({
263
+ topicArn: 'arn:aws:sns:us-east-1:123456789:events',
264
+ region: 'us-east-1',
265
+ });
266
+ const publisher = new SNSPublisher(snsConnection);
267
+
268
+ // Subscriber receives from SQS (queue must be subscribed to SNS topic)
269
+ const sqsConnection = new SQSConnection({
270
+ queueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/my-queue',
271
+ region: 'us-east-1',
272
+ });
273
+ const subscriber = new SQSSubscriber(sqsConnection, {
274
+ expectedTopicArn: 'arn:aws:sns:us-east-1:123456789:events', // Verify messages
275
+ });
276
+
277
+ await subscriber.subscribe(['user.created'], async (message) => {
278
+ // Automatically parses SNS wrapper
279
+ console.log('Received:', message.payload);
280
+ });
281
+ ```
282
+
283
+ ### Sharing Connections
284
+
285
+ You can share connections between publishers and subscribers:
286
+
287
+ ```typescript
288
+ import { EventConnectionFactory, Publisher, Subscriber } from '@geekmidas/events';
289
+
290
+ const connection = await EventConnectionFactory.create(
291
+ 'rabbitmq://localhost:5672?exchange=events'
292
+ );
293
+
294
+ const publisher = await Publisher.fromConnection(connection);
295
+ const subscriber = await Subscriber.fromConnection(connection);
296
+ ```
297
+
298
+ ### Message Filtering
299
+
300
+ Subscribers only receive messages they're subscribed to:
301
+
302
+ ```typescript
303
+ // Only receives user.created and user.updated
304
+ await subscriber.subscribe(['user.created', 'user.updated'], async (message) => {
305
+ if (message.type === 'user.created') {
306
+ console.log('New user:', message.payload.userId);
307
+ } else {
308
+ console.log('User updated:', message.payload.userId);
309
+ }
310
+ });
311
+ ```
312
+
313
+ ### Error Handling
314
+
315
+ ```typescript
316
+ await subscriber.subscribe(['user.created'], async (message) => {
317
+ try {
318
+ await processUser(message.payload);
319
+ } catch (error) {
320
+ console.error('Failed to process user:', error);
321
+ // For RabbitMQ: message will be nack'd and requeued
322
+ // For SQS: message visibility timeout will expire and retry
323
+ throw error;
324
+ }
325
+ });
326
+ ```
327
+
328
+ ## Architecture Patterns
329
+
330
+ ### Fan-Out (SNS → Multiple SQS Queues)
331
+
332
+ ```
333
+ ┌─────────────┐
334
+ │ SNS Topic │
335
+ └──────┬──────┘
336
+ │
337
+ ┌──────────────┼──────────────┐
338
+ │ │ │
339
+ ┌────▼────┐ ┌───▼─────┐ ┌───▼─────┐
340
+ │ Queue 1 │ │ Queue 2 │ │ Queue 3 │
341
+ └────┬────┘ └────┬────┘ └────┬────┘
342
+ │ │ │
343
+ ┌────▼────┐ ┌───▼─────┐ ┌───▼─────┐
344
+ │Service 1│ │Service 2│ │Service 3│
345
+ └─────────┘ └─────────┘ └─────────┘
346
+ ```
347
+
348
+ ### Direct Queue (SQS)
349
+
350
+ ```
351
+ ┌───────────┐ ┌────────────┐ ┌──────────┐
352
+ │ Publisher │─────▶│ SQS Queue │─────▶│Subscriber│
353
+ └───────────┘ └────────────┘ └──────────┘
354
+ ```
355
+
356
+ ### Managed Subscription (SNS Subscriber)
357
+
358
+ ```
359
+ ┌───────────┐ ┌───────────┐ ┌─────────────┐ ┌──────────┐
360
+ │ Publisher │─────▶│ SNS Topic │─────▶│ SQS Queue │─────▶│Subscriber│
361
+ └───────────┘ └───────────┘ │(auto-managed)│ └──────────┘
362
+ └─────────────┘
363
+ ```
364
+
365
+ ## Testing
366
+
367
+ Use the Basic implementation for testing:
368
+
369
+ ```typescript
370
+ import { BasicConnection, BasicPublisher, BasicSubscriber } from '@geekmidas/events/basic';
371
+ import { describe, it, expect } from 'vitest';
372
+
373
+ describe('Event processing', () => {
374
+ it('should process user events', async () => {
375
+ const connection = new BasicConnection();
376
+ const publisher = new BasicPublisher(connection);
377
+ const subscriber = new BasicSubscriber(connection);
378
+
379
+ const received = [];
380
+ await subscriber.subscribe(['user.created'], async (message) => {
381
+ received.push(message);
382
+ });
383
+
384
+ await publisher.publish([
385
+ { type: 'user.created', payload: { userId: '123' } }
386
+ ]);
387
+
388
+ expect(received).toHaveLength(1);
389
+ expect(received[0].payload.userId).toBe('123');
390
+ });
391
+ });
392
+ ```
393
+
394
+ ## LocalStack Setup
395
+
396
+ For local development with SQS/SNS, use LocalStack:
397
+
398
+ ```yaml
399
+ # docker-compose.yml
400
+ services:
401
+ localstack:
402
+ image: localstack/localstack:latest
403
+ ports:
404
+ - "4566:4566"
405
+ environment:
406
+ - SERVICES=sqs,sns
407
+ - DEBUG=1
408
+ ```
409
+
410
+ Then use `endpoint: 'http://localhost:4566'` in your configuration.
411
+
412
+ ## API Reference
413
+
414
+ ### Publisher
415
+
416
+ ```typescript
417
+ class Publisher {
418
+ static fromConnectionString<T>(connectionStr: string): Promise<EventPublisher<T>>
419
+ static fromConnection<T>(connection: EventConnection): Promise<EventPublisher<T>>
420
+ }
421
+ ```
422
+
423
+ ### Subscriber
424
+
425
+ ```typescript
426
+ class Subscriber {
427
+ static fromConnectionString<T>(connectionStr: string): Promise<EventSubscriber<T>>
428
+ static fromConnection<T>(connection: EventConnection): Promise<EventSubscriber<T>>
429
+ }
430
+ ```
431
+
432
+ ### EventPublisher
433
+
434
+ ```typescript
435
+ interface EventPublisher<TMessage> {
436
+ publish(messages: TMessage[]): Promise<void>
437
+ }
438
+ ```
439
+
440
+ ### EventSubscriber
441
+
442
+ ```typescript
443
+ interface EventSubscriber<TMessage> {
444
+ subscribe(
445
+ messages: TMessage['type'][],
446
+ listener: (payload: TMessage) => Promise<void>
447
+ ): Promise<void>
448
+ stop(): void | Promise<void>
449
+ }
450
+ ```
451
+
452
+ ### PublishableMessage
453
+
454
+ ```typescript
455
+ type PublishableMessage<TType extends string, TPayload> = {
456
+ type: TType
457
+ payload: TPayload
458
+ }
459
+ ```
460
+
461
+ ## Best Practices
462
+
463
+ 1. **Define Message Types**: Use TypeScript union types for all your message types
464
+ 2. **Message Type Naming**: Use dot notation for namespacing (e.g., `user.created`, `order.shipped`)
465
+ 3. **Error Handling**: Always handle errors in your message listeners
466
+ 4. **Connection Pooling**: Reuse connections when possible
467
+ 5. **Testing**: Use Basic implementation for unit tests, real backends for integration tests
468
+ 6. **Cleanup**: Always call `stop()` on subscribers when shutting down
469
+ 7. **Message Versioning**: Include version in message type for breaking changes (e.g., `user.created.v2`)
470
+
471
+ ## License
472
+
473
+ MIT
@@ -0,0 +1,42 @@
1
+ import { EventPublisherType } from "./types-B63qHVOG.mjs";
2
+ import { EventEmitter } from "node:events";
3
+
4
+ //#region src/basic/BasicConnection.ts
5
+ var BasicConnection = class BasicConnection {
6
+ type = EventPublisherType.Basic;
7
+ emitter;
8
+ connected = false;
9
+ constructor() {
10
+ this.emitter = new EventEmitter();
11
+ }
12
+ /**
13
+ * Create a BasicConnection from a connection string
14
+ * Format: basic://
15
+ */
16
+ static async fromConnectionString(_connectionString) {
17
+ const connection = new BasicConnection();
18
+ await connection.connect();
19
+ return connection;
20
+ }
21
+ async connect() {
22
+ this.connected = true;
23
+ }
24
+ async close() {
25
+ this.emitter.removeAllListeners();
26
+ this.connected = false;
27
+ }
28
+ isConnected() {
29
+ return this.connected;
30
+ }
31
+ /**
32
+ * Get the underlying EventEmitter
33
+ * Used by BasicPublisher and BasicSubscriber
34
+ */
35
+ get eventEmitter() {
36
+ return this.emitter;
37
+ }
38
+ };
39
+
40
+ //#endregion
41
+ export { BasicConnection };
42
+ //# sourceMappingURL=BasicConnection-BNi6_olg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BasicConnection-BNi6_olg.mjs","names":["_connectionString: string"],"sources":["../src/basic/BasicConnection.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport class BasicConnection implements EventConnection {\n readonly type = EventPublisherType.Basic;\n private emitter: EventEmitter;\n private connected = false;\n\n constructor() {\n this.emitter = new EventEmitter();\n }\n\n /**\n * Create a BasicConnection from a connection string\n * Format: basic://\n */\n static async fromConnectionString(\n _connectionString: string,\n ): Promise<BasicConnection> {\n // Basic connection doesn't need any configuration\n const connection = new BasicConnection();\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.emitter.removeAllListeners();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying EventEmitter\n * Used by BasicPublisher and BasicSubscriber\n */\n get eventEmitter(): EventEmitter {\n return this.emitter;\n }\n}\n"],"mappings":";;;;AAIA,IAAa,kBAAb,MAAa,gBAA2C;CACtD,AAAS,OAAO,mBAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CAEpB,cAAc;AACZ,OAAK,UAAU,IAAI;CACpB;;;;;CAMD,aAAa,qBACXA,mBAC0B;EAE1B,MAAM,aAAa,IAAI;AACvB,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAC7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,QAAQ,oBAAoB;AACjC,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;;CAMD,IAAI,eAA6B;AAC/B,SAAO,KAAK;CACb;AACF"}
@@ -0,0 +1,26 @@
1
+ import { EventConnection, EventPublisherType } from "./types-D0g-o6wX.mjs";
2
+ import { EventEmitter } from "node:events";
3
+
4
+ //#region src/basic/BasicConnection.d.ts
5
+ declare class BasicConnection implements EventConnection {
6
+ readonly type = EventPublisherType.Basic;
7
+ private emitter;
8
+ private connected;
9
+ constructor();
10
+ /**
11
+ * Create a BasicConnection from a connection string
12
+ * Format: basic://
13
+ */
14
+ static fromConnectionString(_connectionString: string): Promise<BasicConnection>;
15
+ connect(): Promise<void>;
16
+ close(): Promise<void>;
17
+ isConnected(): boolean;
18
+ /**
19
+ * Get the underlying EventEmitter
20
+ * Used by BasicPublisher and BasicSubscriber
21
+ */
22
+ get eventEmitter(): EventEmitter;
23
+ }
24
+ //#endregion
25
+ export { BasicConnection };
26
+ //# sourceMappingURL=BasicConnection-C4_Y0nIj.d.mts.map
@@ -0,0 +1,26 @@
1
+ import { EventConnection, EventPublisherType } from "./types-DEGUg5BA.cjs";
2
+ import { EventEmitter } from "node:events";
3
+
4
+ //#region src/basic/BasicConnection.d.ts
5
+ declare class BasicConnection implements EventConnection {
6
+ readonly type = EventPublisherType.Basic;
7
+ private emitter;
8
+ private connected;
9
+ constructor();
10
+ /**
11
+ * Create a BasicConnection from a connection string
12
+ * Format: basic://
13
+ */
14
+ static fromConnectionString(_connectionString: string): Promise<BasicConnection>;
15
+ connect(): Promise<void>;
16
+ close(): Promise<void>;
17
+ isConnected(): boolean;
18
+ /**
19
+ * Get the underlying EventEmitter
20
+ * Used by BasicPublisher and BasicSubscriber
21
+ */
22
+ get eventEmitter(): EventEmitter;
23
+ }
24
+ //#endregion
25
+ export { BasicConnection };
26
+ //# sourceMappingURL=BasicConnection-CddnULuT.d.cts.map
@@ -0,0 +1,48 @@
1
+ const require_chunk = require('./chunk-CUT6urMc.cjs');
2
+ const require_types = require('./types-CvSMEO5z.cjs');
3
+ const node_events = require_chunk.__toESM(require("node:events"));
4
+
5
+ //#region src/basic/BasicConnection.ts
6
+ var BasicConnection = class BasicConnection {
7
+ type = require_types.EventPublisherType.Basic;
8
+ emitter;
9
+ connected = false;
10
+ constructor() {
11
+ this.emitter = new node_events.EventEmitter();
12
+ }
13
+ /**
14
+ * Create a BasicConnection from a connection string
15
+ * Format: basic://
16
+ */
17
+ static async fromConnectionString(_connectionString) {
18
+ const connection = new BasicConnection();
19
+ await connection.connect();
20
+ return connection;
21
+ }
22
+ async connect() {
23
+ this.connected = true;
24
+ }
25
+ async close() {
26
+ this.emitter.removeAllListeners();
27
+ this.connected = false;
28
+ }
29
+ isConnected() {
30
+ return this.connected;
31
+ }
32
+ /**
33
+ * Get the underlying EventEmitter
34
+ * Used by BasicPublisher and BasicSubscriber
35
+ */
36
+ get eventEmitter() {
37
+ return this.emitter;
38
+ }
39
+ };
40
+
41
+ //#endregion
42
+ Object.defineProperty(exports, 'BasicConnection', {
43
+ enumerable: true,
44
+ get: function () {
45
+ return BasicConnection;
46
+ }
47
+ });
48
+ //# sourceMappingURL=BasicConnection-CzfMubv_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BasicConnection-CzfMubv_.cjs","names":["EventPublisherType","EventEmitter","_connectionString: string"],"sources":["../src/basic/BasicConnection.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\nimport type { EventConnection } from '../types';\nimport { EventPublisherType } from '../types';\n\nexport class BasicConnection implements EventConnection {\n readonly type = EventPublisherType.Basic;\n private emitter: EventEmitter;\n private connected = false;\n\n constructor() {\n this.emitter = new EventEmitter();\n }\n\n /**\n * Create a BasicConnection from a connection string\n * Format: basic://\n */\n static async fromConnectionString(\n _connectionString: string,\n ): Promise<BasicConnection> {\n // Basic connection doesn't need any configuration\n const connection = new BasicConnection();\n await connection.connect();\n return connection;\n }\n\n async connect(): Promise<void> {\n this.connected = true;\n }\n\n async close(): Promise<void> {\n this.emitter.removeAllListeners();\n this.connected = false;\n }\n\n isConnected(): boolean {\n return this.connected;\n }\n\n /**\n * Get the underlying EventEmitter\n * Used by BasicPublisher and BasicSubscriber\n */\n get eventEmitter(): EventEmitter {\n return this.emitter;\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,kBAAb,MAAa,gBAA2C;CACtD,AAAS,OAAOA,iCAAmB;CACnC,AAAQ;CACR,AAAQ,YAAY;CAEpB,cAAc;AACZ,OAAK,UAAU,IAAIC;CACpB;;;;;CAMD,aAAa,qBACXC,mBAC0B;EAE1B,MAAM,aAAa,IAAI;AACvB,QAAM,WAAW,SAAS;AAC1B,SAAO;CACR;CAED,MAAM,UAAyB;AAC7B,OAAK,YAAY;CAClB;CAED,MAAM,QAAuB;AAC3B,OAAK,QAAQ,oBAAoB;AACjC,OAAK,YAAY;CAClB;CAED,cAAuB;AACrB,SAAO,KAAK;CACb;;;;;CAMD,IAAI,eAA6B;AAC/B,SAAO,KAAK;CACb;AACF"}
@@ -0,0 +1,20 @@
1
+
2
+ //#region src/basic/BasicPublisher.ts
3
+ var BasicPublisher = class {
4
+ constructor(connection) {
5
+ this.connection = connection;
6
+ }
7
+ async publish(messages) {
8
+ const emitter = this.connection.eventEmitter;
9
+ for (const message of messages) emitter.emit(message.type, message);
10
+ }
11
+ };
12
+
13
+ //#endregion
14
+ Object.defineProperty(exports, 'BasicPublisher', {
15
+ enumerable: true,
16
+ get: function () {
17
+ return BasicPublisher;
18
+ }
19
+ });
20
+ //# sourceMappingURL=BasicPublisher-BzfzHIfb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BasicPublisher-BzfzHIfb.cjs","names":["connection: BasicConnection","messages: TMessage[]"],"sources":["../src/basic/BasicPublisher.ts"],"sourcesContent":["import type { EventPublisher, PublishableMessage } from '../types';\nimport type { BasicConnection } from './BasicConnection';\n\nexport class BasicPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n constructor(private connection: BasicConnection) {}\n\n async publish(messages: TMessage[]) {\n const emitter = this.connection.eventEmitter;\n for (const message of messages) {\n emitter.emit(message.type, message);\n }\n }\n}\n"],"mappings":";;AAGA,IAAa,iBAAb,MAEA;CACE,YAAoBA,YAA6B;EAA7B;CAA+B;CAEnD,MAAM,QAAQC,UAAsB;EAClC,MAAM,UAAU,KAAK,WAAW;AAChC,OAAK,MAAM,WAAW,SACpB,SAAQ,KAAK,QAAQ,MAAM,QAAQ;CAEtC;AACF"}
@@ -0,0 +1,14 @@
1
+ //#region src/basic/BasicPublisher.ts
2
+ var BasicPublisher = class {
3
+ constructor(connection) {
4
+ this.connection = connection;
5
+ }
6
+ async publish(messages) {
7
+ const emitter = this.connection.eventEmitter;
8
+ for (const message of messages) emitter.emit(message.type, message);
9
+ }
10
+ };
11
+
12
+ //#endregion
13
+ export { BasicPublisher };
14
+ //# sourceMappingURL=BasicPublisher-Cg3SxIVR.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BasicPublisher-Cg3SxIVR.mjs","names":["connection: BasicConnection","messages: TMessage[]"],"sources":["../src/basic/BasicPublisher.ts"],"sourcesContent":["import type { EventPublisher, PublishableMessage } from '../types';\nimport type { BasicConnection } from './BasicConnection';\n\nexport class BasicPublisher<TMessage extends PublishableMessage<string, any>>\n implements EventPublisher<TMessage>\n{\n constructor(private connection: BasicConnection) {}\n\n async publish(messages: TMessage[]) {\n const emitter = this.connection.eventEmitter;\n for (const message of messages) {\n emitter.emit(message.type, message);\n }\n }\n}\n"],"mappings":";AAGA,IAAa,iBAAb,MAEA;CACE,YAAoBA,YAA6B;EAA7B;CAA+B;CAEnD,MAAM,QAAQC,UAAsB;EAClC,MAAM,UAAU,KAAK,WAAW;AAChC,OAAK,MAAM,WAAW,SACpB,SAAQ,KAAK,QAAQ,MAAM,QAAQ;CAEtC;AACF"}
@@ -0,0 +1,12 @@
1
+ import { EventPublisher, PublishableMessage } from "./types-D0g-o6wX.mjs";
2
+ import { BasicConnection } from "./BasicConnection-C4_Y0nIj.mjs";
3
+
4
+ //#region src/basic/BasicPublisher.d.ts
5
+ declare class BasicPublisher<TMessage extends PublishableMessage<string, any>> implements EventPublisher<TMessage> {
6
+ private connection;
7
+ constructor(connection: BasicConnection);
8
+ publish(messages: TMessage[]): Promise<void>;
9
+ }
10
+ //#endregion
11
+ export { BasicPublisher };
12
+ //# sourceMappingURL=BasicPublisher-DCBxaTwC.d.mts.map