rabbitmq-sdk 0.0.1-security → 1.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.

Potentially problematic release.


This version of rabbitmq-sdk might be problematic. Click here for more details.

Files changed (140) hide show
  1. package/.eslintrc.js +23 -0
  2. package/.kiro/specs/sdk-rabbitmq/design.md +369 -0
  3. package/.kiro/specs/sdk-rabbitmq/requirements.md +97 -0
  4. package/.kiro/specs/sdk-rabbitmq/tasks.md +248 -0
  5. package/README.md +273 -5
  6. package/bun.lock +790 -0
  7. package/config.example.json +13 -0
  8. package/dist/components/ConfigurationManager.d.ts +35 -0
  9. package/dist/components/ConfigurationManager.d.ts.map +1 -0
  10. package/dist/components/ConfigurationManager.js +118 -0
  11. package/dist/components/ConfigurationManager.js.map +1 -0
  12. package/dist/components/ConnectionManager.d.ts +93 -0
  13. package/dist/components/ConnectionManager.d.ts.map +1 -0
  14. package/dist/components/ConnectionManager.js +349 -0
  15. package/dist/components/ConnectionManager.js.map +1 -0
  16. package/dist/components/DLQHandler.d.ts +81 -0
  17. package/dist/components/DLQHandler.d.ts.map +1 -0
  18. package/dist/components/DLQHandler.js +228 -0
  19. package/dist/components/DLQHandler.js.map +1 -0
  20. package/dist/components/Logger.d.ts +77 -0
  21. package/dist/components/Logger.d.ts.map +1 -0
  22. package/dist/components/Logger.js +193 -0
  23. package/dist/components/Logger.js.map +1 -0
  24. package/dist/components/MessagePublisher.d.ts +49 -0
  25. package/dist/components/MessagePublisher.d.ts.map +1 -0
  26. package/dist/components/MessagePublisher.js +158 -0
  27. package/dist/components/MessagePublisher.js.map +1 -0
  28. package/dist/components/MessageSubscriber.d.ts +108 -0
  29. package/dist/components/MessageSubscriber.d.ts.map +1 -0
  30. package/dist/components/MessageSubscriber.js +503 -0
  31. package/dist/components/MessageSubscriber.js.map +1 -0
  32. package/dist/components/ResourceCreator.d.ts +89 -0
  33. package/dist/components/ResourceCreator.d.ts.map +1 -0
  34. package/dist/components/ResourceCreator.js +352 -0
  35. package/dist/components/ResourceCreator.js.map +1 -0
  36. package/dist/components/SdkRabbitmq.d.ts +103 -0
  37. package/dist/components/SdkRabbitmq.d.ts.map +1 -0
  38. package/dist/components/SdkRabbitmq.js +364 -0
  39. package/dist/components/SdkRabbitmq.js.map +1 -0
  40. package/dist/components/index.d.ts +9 -0
  41. package/dist/components/index.d.ts.map +1 -0
  42. package/dist/components/index.js +20 -0
  43. package/dist/components/index.js.map +1 -0
  44. package/dist/index.d.ts +5 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +27 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/interfaces/IConfiguration.d.ts +35 -0
  49. package/dist/interfaces/IConfiguration.d.ts.map +1 -0
  50. package/dist/interfaces/IConfiguration.js +3 -0
  51. package/dist/interfaces/IConfiguration.js.map +1 -0
  52. package/dist/interfaces/IConnection.d.ts +21 -0
  53. package/dist/interfaces/IConnection.d.ts.map +1 -0
  54. package/dist/interfaces/IConnection.js +3 -0
  55. package/dist/interfaces/IConnection.js.map +1 -0
  56. package/dist/interfaces/IDLQ.d.ts +12 -0
  57. package/dist/interfaces/IDLQ.d.ts.map +1 -0
  58. package/dist/interfaces/IDLQ.js +3 -0
  59. package/dist/interfaces/IDLQ.js.map +1 -0
  60. package/dist/interfaces/IErrors.d.ts +33 -0
  61. package/dist/interfaces/IErrors.d.ts.map +1 -0
  62. package/dist/interfaces/IErrors.js +56 -0
  63. package/dist/interfaces/IErrors.js.map +1 -0
  64. package/dist/interfaces/ILogger.d.ts +14 -0
  65. package/dist/interfaces/ILogger.d.ts.map +1 -0
  66. package/dist/interfaces/ILogger.js +3 -0
  67. package/dist/interfaces/ILogger.js.map +1 -0
  68. package/dist/interfaces/IMessage.d.ts +52 -0
  69. package/dist/interfaces/IMessage.d.ts.map +1 -0
  70. package/dist/interfaces/IMessage.js +3 -0
  71. package/dist/interfaces/IMessage.js.map +1 -0
  72. package/dist/interfaces/IResource.d.ts +31 -0
  73. package/dist/interfaces/IResource.d.ts.map +1 -0
  74. package/dist/interfaces/IResource.js +3 -0
  75. package/dist/interfaces/IResource.js.map +1 -0
  76. package/dist/interfaces/ISdkRabbitmq.d.ts +17 -0
  77. package/dist/interfaces/ISdkRabbitmq.d.ts.map +1 -0
  78. package/dist/interfaces/ISdkRabbitmq.js +3 -0
  79. package/dist/interfaces/ISdkRabbitmq.js.map +1 -0
  80. package/dist/interfaces/index.d.ts +9 -0
  81. package/dist/interfaces/index.d.ts.map +1 -0
  82. package/dist/interfaces/index.js +33 -0
  83. package/dist/interfaces/index.js.map +1 -0
  84. package/dist/utils/configSchema.d.ts +8 -0
  85. package/dist/utils/configSchema.d.ts.map +1 -0
  86. package/dist/utils/configSchema.js +51 -0
  87. package/dist/utils/configSchema.js.map +1 -0
  88. package/docker-compose.yml +24 -0
  89. package/example.ts +65 -0
  90. package/examples/README-dynamic-routing.md +155 -0
  91. package/examples/bind-unbind-example.js +56 -0
  92. package/examples/test-chatbot-exchange.ts +83 -0
  93. package/examples/test-dynamic-routing-flow.js +299 -0
  94. package/examples/test-dynamic-routing-flow.ts +355 -0
  95. package/examples/test-no-disconnect.ts +0 -0
  96. package/examples/test-raw-rabbitmq.js +68 -0
  97. package/examples/test-same-channel.ts +81 -0
  98. package/examples/test-schedule-flow.ts +713 -0
  99. package/examples/test-simple-greeting.ts +66 -0
  100. package/examples/test-simple-schedule.ts +76 -0
  101. package/examples/test-wildcard.ts +364 -0
  102. package/jest.config.js +17 -0
  103. package/package.json +42 -4
  104. package/preinstall.js +1 -0
  105. package/prompts/test-dynamic-routing-flow.md +46 -0
  106. package/run.js +4 -0
  107. package/scripts/run-dynamic-routing-test.ts +31 -0
  108. package/src/.gitkeep +1 -0
  109. package/src/components/.gitkeep +1 -0
  110. package/src/components/ConfigurationManager.ts +104 -0
  111. package/src/components/ConnectionManager.ts +357 -0
  112. package/src/components/DLQHandler.ts +271 -0
  113. package/src/components/Logger.ts +224 -0
  114. package/src/components/MessagePublisher.ts +180 -0
  115. package/src/components/MessageSubscriber.ts +597 -0
  116. package/src/components/ResourceCreator.ts +411 -0
  117. package/src/components/SdkRabbitmq.ts +443 -0
  118. package/src/components/__tests__/ConfigurationManager.test.ts +357 -0
  119. package/src/components/__tests__/ConnectionManager.test.ts +387 -0
  120. package/src/components/__tests__/DLQHandler.test.ts +399 -0
  121. package/src/components/__tests__/Logger.test.ts +354 -0
  122. package/src/components/__tests__/MessagePublisher.test.ts +337 -0
  123. package/src/components/__tests__/MessageSubscriber.test.ts +542 -0
  124. package/src/components/__tests__/ResourceCreator.test.ts +465 -0
  125. package/src/components/__tests__/SdkRabbitmq.integration.test.ts +433 -0
  126. package/src/components/index.ts +8 -0
  127. package/src/index.ts +11 -0
  128. package/src/interfaces/.gitkeep +1 -0
  129. package/src/interfaces/IConfiguration.ts +38 -0
  130. package/src/interfaces/IConnection.ts +27 -0
  131. package/src/interfaces/IDLQ.ts +13 -0
  132. package/src/interfaces/IErrors.ts +53 -0
  133. package/src/interfaces/ILogger.ts +16 -0
  134. package/src/interfaces/IMessage.ts +65 -0
  135. package/src/interfaces/IResource.ts +35 -0
  136. package/src/interfaces/ISdkRabbitmq.ts +26 -0
  137. package/src/interfaces/index.ts +23 -0
  138. package/src/utils/.gitkeep +1 -0
  139. package/src/utils/configSchema.ts +58 -0
  140. package/tsconfig.json +34 -0
@@ -0,0 +1,443 @@
1
+ import { ISdkRabbitmq } from '../interfaces/ISdkRabbitmq';
2
+ import { MessageCallback } from '../interfaces/IMessage';
3
+ import { ConfigurationManager } from './ConfigurationManager';
4
+ import { ConnectionManager } from './ConnectionManager';
5
+ import { MessagePublisher } from './MessagePublisher';
6
+ import { MessageSubscriber } from './MessageSubscriber';
7
+ import { ResourceCreator } from './ResourceCreator';
8
+ import { DLQHandler } from './DLQHandler';
9
+ import { Logger } from './Logger';
10
+ import { IConfiguration } from '../interfaces/IConfiguration';
11
+
12
+ /**
13
+ * Main SdkRabbitmq singleton class that provides the public API
14
+ * Implements singleton pattern to ensure single connection per application
15
+ */
16
+ export class SdkRabbitmq implements ISdkRabbitmq {
17
+ private static instance: SdkRabbitmq | null = null;
18
+ private static isInitializing = false;
19
+
20
+ private configurationManager: ConfigurationManager;
21
+ private connectionManager!: ConnectionManager;
22
+ private messagePublisher!: MessagePublisher;
23
+ private messageSubscriber!: MessageSubscriber;
24
+ private resourceCreator!: ResourceCreator;
25
+ private dlqHandler!: DLQHandler;
26
+ private logger: Logger;
27
+ private config: IConfiguration;
28
+ private isInitialized = false;
29
+
30
+ /**
31
+ * Private constructor to enforce singleton pattern
32
+ */
33
+ private constructor() {
34
+ // Initialize configuration manager first
35
+ this.configurationManager = ConfigurationManager.getInstance();
36
+
37
+ // Load configuration
38
+ this.config = this.configurationManager.loadConfig();
39
+
40
+ // Initialize logger with configuration
41
+ this.logger = Logger.createComponentLogger('SdkRabbitmq', this.config.logging);
42
+
43
+ this.logger.info('SdkRabbitmq instance created', {
44
+ url: this.config.url,
45
+ dlqActive: this.config.dlq.active
46
+ });
47
+ }
48
+
49
+ /**
50
+ * Get singleton instance of SdkRabbitmq
51
+ * Returns existing instance if available, creates new one otherwise
52
+ */
53
+ public static async getInstance(): Promise<SdkRabbitmq> {
54
+ // Return existing instance if available and initialized
55
+ if (SdkRabbitmq.instance && SdkRabbitmq.instance.isInitialized) {
56
+ return SdkRabbitmq.instance;
57
+ }
58
+
59
+ // Prevent multiple simultaneous initialization attempts
60
+ if (SdkRabbitmq.isInitializing) {
61
+ // Wait for initialization to complete
62
+ return new Promise<SdkRabbitmq>((resolve) => {
63
+ const checkInitialization = (): void => {
64
+ if (SdkRabbitmq.instance && SdkRabbitmq.instance.isInitialized && !SdkRabbitmq.isInitializing) {
65
+ resolve(SdkRabbitmq.instance);
66
+ } else {
67
+ setTimeout(checkInitialization, 50);
68
+ }
69
+ };
70
+ checkInitialization();
71
+ });
72
+ }
73
+
74
+ SdkRabbitmq.isInitializing = true;
75
+
76
+ try {
77
+ // Create instance if it doesn't exist
78
+ if (!SdkRabbitmq.instance) {
79
+ SdkRabbitmq.instance = new SdkRabbitmq();
80
+ }
81
+
82
+ // Initialize all components
83
+ await SdkRabbitmq.instance.initializeComponents();
84
+
85
+ SdkRabbitmq.instance.isInitialized = true;
86
+ SdkRabbitmq.isInitializing = false;
87
+
88
+ return SdkRabbitmq.instance;
89
+ } catch (error) {
90
+ SdkRabbitmq.isInitializing = false;
91
+ SdkRabbitmq.instance = null;
92
+ throw error;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Initialize all SDK components and establish connection
98
+ */
99
+ private async initializeComponents(): Promise<void> {
100
+ try {
101
+ this.logger.info('Initializing SDK components');
102
+
103
+ // Initialize connection manager
104
+ this.connectionManager = ConnectionManager.getInstance(this.config);
105
+
106
+ // Initialize resource creator
107
+ this.resourceCreator = new ResourceCreator(this.connectionManager, this.config.logging);
108
+
109
+ // Initialize DLQ handler
110
+ this.dlqHandler = new DLQHandler(this.config, this.connectionManager, this.resourceCreator);
111
+
112
+ // Initialize message publisher
113
+ this.messagePublisher = new MessagePublisher(
114
+ this.connectionManager,
115
+ this.resourceCreator,
116
+ this.config.logging
117
+ );
118
+
119
+ // Initialize message subscriber
120
+ this.messageSubscriber = new MessageSubscriber(
121
+ this.connectionManager,
122
+ this.resourceCreator,
123
+ this.dlqHandler,
124
+ this.config.logging
125
+ );
126
+
127
+ // Establish connection to RabbitMQ
128
+ await this.connectionManager.connect();
129
+
130
+ this.logger.info('SDK components initialized successfully');
131
+ } catch (error) {
132
+ this.logger.logError('Failed to initialize SDK components', error as Error);
133
+ throw error;
134
+ }
135
+ }
136
+ /**
137
+
138
+ * Publish a message to an exchange
139
+ * @param exchange Exchange name (required)
140
+ * @param routingKey Routing key for message routing (required)
141
+ * @param payload Message payload to be JSON serialized (required)
142
+ * @returns Promise<boolean> - true for success, false for failure
143
+ */
144
+ public async publish(exchange: string, routingKey: string, payload: any): Promise<boolean> {
145
+ // Parameter validation at API level
146
+ this.validatePublishParameters(exchange, routingKey, payload);
147
+
148
+ if (!this.isInitialized) {
149
+ throw new Error('SDK is not initialized. Please wait for initialization to complete.');
150
+ }
151
+
152
+ try {
153
+ this.logger.logOperation('publish', 'Publishing message', {
154
+ exchange,
155
+ routingKey,
156
+ payloadType: typeof payload
157
+ });
158
+
159
+ // Delegate to MessagePublisher
160
+ const result = await this.messagePublisher.publish(exchange, routingKey, payload);
161
+
162
+ this.logger.logOperation('publish', 'Message published successfully', {
163
+ exchange,
164
+ routingKey,
165
+ success: result
166
+ });
167
+
168
+ return result;
169
+ } catch (error) {
170
+ this.logger.logError('Failed to publish message', error as Error, {
171
+ exchange,
172
+ routingKey,
173
+ payloadType: typeof payload
174
+ });
175
+ return false;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Subscribe to messages from a queue
181
+ * @param exchange Exchange name (required)
182
+ * @param queue Queue name (required)
183
+ * @param routingKey Routing key for binding (required)
184
+ * @param callback Message callback function (required)
185
+ */
186
+ public async subscribe(
187
+ exchange: string,
188
+ queue: string,
189
+ routingKey: string,
190
+ callback: MessageCallback
191
+ ): Promise<void> {
192
+ // Parameter validation at API level
193
+ this.validateSubscribeParameters(exchange, queue, routingKey, callback);
194
+
195
+ if (!this.isInitialized) {
196
+ throw new Error('SDK is not initialized. Please wait for initialization to complete.');
197
+ }
198
+
199
+ try {
200
+ this.logger.logOperation('subscribe', 'Setting up subscription', {
201
+ exchange,
202
+ queue,
203
+ routingKey
204
+ });
205
+
206
+ // Delegate to MessageSubscriber
207
+ await this.messageSubscriber.subscribe(exchange, queue, routingKey, callback);
208
+
209
+ this.logger.logOperation('subscribe', 'Subscription established successfully', {
210
+ exchange,
211
+ queue,
212
+ routingKey
213
+ });
214
+ } catch (error) {
215
+ this.logger.logError('Failed to subscribe to messages', error as Error, {
216
+ exchange,
217
+ queue,
218
+ routingKey
219
+ });
220
+ throw error;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Disconnect from RabbitMQ gracefully
226
+ * Performs cleanup of all components and closes connection
227
+ */
228
+ public async disconnect(): Promise<void> {
229
+ if (!this.isInitialized) {
230
+ this.logger.warn('SDK is not initialized, nothing to disconnect');
231
+ return;
232
+ }
233
+
234
+ try {
235
+ this.logger.info('Starting graceful shutdown');
236
+
237
+ // Cleanup message subscriber (unsubscribe from all queues)
238
+ if (this.messageSubscriber) {
239
+ await this.messageSubscriber.cleanup();
240
+ }
241
+
242
+ // Disconnect from RabbitMQ
243
+ if (this.connectionManager) {
244
+ await this.connectionManager.disconnect();
245
+ }
246
+
247
+ // Clear component caches
248
+ if (this.resourceCreator) {
249
+ this.resourceCreator.clearCache();
250
+ }
251
+
252
+ if (this.dlqHandler) {
253
+ this.dlqHandler.clearCache();
254
+ }
255
+
256
+ // Reset initialization state
257
+ this.isInitialized = false;
258
+
259
+ this.logger.info('SDK disconnected successfully');
260
+ } catch (error) {
261
+ this.logger.logError('Error during disconnect', error as Error);
262
+ throw error;
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Validate publish method parameters
268
+ * @param exchange Exchange name
269
+ * @param routingKey Routing key
270
+ * @param payload Message payload
271
+ */
272
+ private validatePublishParameters(exchange: string, routingKey: string, payload: any): void {
273
+ if (!exchange || typeof exchange !== 'string') {
274
+ throw new Error('Exchange parameter is required and must be a non-empty string');
275
+ }
276
+
277
+ if (!routingKey || typeof routingKey !== 'string') {
278
+ throw new Error('RoutingKey parameter is required and must be a non-empty string');
279
+ }
280
+
281
+ if (payload === undefined || payload === null) {
282
+ throw new Error('Payload parameter is required and cannot be null or undefined');
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Validate subscribe method parameters
288
+ * @param exchange Exchange name
289
+ * @param queue Queue name
290
+ * @param routingKey Routing key
291
+ * @param callback Message callback function
292
+ */
293
+ private validateSubscribeParameters(
294
+ exchange: string,
295
+ queue: string,
296
+ routingKey: string,
297
+ callback: MessageCallback
298
+ ): void {
299
+ if (!exchange || typeof exchange !== 'string') {
300
+ throw new Error('Exchange parameter is required and must be a non-empty string');
301
+ }
302
+
303
+ if (!queue || typeof queue !== 'string') {
304
+ throw new Error('Queue parameter is required and must be a non-empty string');
305
+ }
306
+
307
+ if (!routingKey || typeof routingKey !== 'string') {
308
+ throw new Error('RoutingKey parameter is required and must be a non-empty string');
309
+ }
310
+
311
+ if (!callback || typeof callback !== 'function') {
312
+ throw new Error('Callback parameter is required and must be a function');
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Validate bind/unbind method parameters
318
+ * @param queue Queue name
319
+ * @param exchange Exchange name
320
+ * @param routingKey Routing key
321
+ */
322
+ private validateBindParameters(queue: string, exchange: string, routingKey: string): void {
323
+ if (!queue || typeof queue !== 'string') {
324
+ throw new Error('Queue parameter is required and must be a non-empty string');
325
+ }
326
+
327
+ if (!exchange || typeof exchange !== 'string') {
328
+ throw new Error('Exchange parameter is required and must be a non-empty string');
329
+ }
330
+
331
+ if (!routingKey || typeof routingKey !== 'string') {
332
+ throw new Error('RoutingKey parameter is required and must be a non-empty string');
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Get SDK initialization status
338
+ */
339
+ public isReady(): boolean {
340
+ return this.isInitialized && this.connectionManager?.isConnected();
341
+ }
342
+
343
+ /**
344
+ * Bind a queue to an exchange with a routing key
345
+ * @param queue Queue name (required)
346
+ * @param exchange Exchange name (required)
347
+ * @param routingKey Routing key for binding (required)
348
+ */
349
+ public async bind(queue: string, exchange: string, routingKey: string): Promise<void> {
350
+ // Parameter validation at API level
351
+ this.validateBindParameters(queue, exchange, routingKey);
352
+
353
+ if (!this.isInitialized) {
354
+ throw new Error('SDK is not initialized. Please wait for initialization to complete.');
355
+ }
356
+
357
+ try {
358
+ this.logger.logOperation('bind', 'Binding queue to exchange', {
359
+ queue,
360
+ exchange,
361
+ routingKey
362
+ });
363
+
364
+ // Delegate to ResourceCreator
365
+ await this.resourceCreator.bindQueue(queue, exchange, routingKey);
366
+
367
+ this.logger.logOperation('bind', 'Queue bound to exchange successfully', {
368
+ queue,
369
+ exchange,
370
+ routingKey
371
+ });
372
+ } catch (error) {
373
+ this.logger.logError('Failed to bind queue to exchange', error as Error, {
374
+ queue,
375
+ exchange,
376
+ routingKey
377
+ });
378
+ throw error;
379
+ }
380
+ }
381
+
382
+ /**
383
+ * Unbind a queue from an exchange with a routing key
384
+ * @param queue Queue name (required)
385
+ * @param exchange Exchange name (required)
386
+ * @param routingKey Routing key for unbinding (required)
387
+ */
388
+ public async unbind(queue: string, exchange: string, routingKey: string): Promise<void> {
389
+ // Parameter validation at API level
390
+ this.validateBindParameters(queue, exchange, routingKey);
391
+
392
+ if (!this.isInitialized) {
393
+ throw new Error('SDK is not initialized. Please wait for initialization to complete.');
394
+ }
395
+
396
+ try {
397
+ this.logger.logOperation('unbind', 'Unbinding queue from exchange', {
398
+ queue,
399
+ exchange,
400
+ routingKey
401
+ });
402
+
403
+ // Delegate to ResourceCreator
404
+ await this.resourceCreator.unbindQueue(queue, exchange, routingKey);
405
+
406
+ this.logger.logOperation('unbind', 'Queue unbound from exchange successfully', {
407
+ queue,
408
+ exchange,
409
+ routingKey
410
+ });
411
+ } catch (error) {
412
+ this.logger.logError('Failed to unbind queue from exchange', error as Error, {
413
+ queue,
414
+ exchange,
415
+ routingKey
416
+ });
417
+ throw error;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Get active consumers for monitoring
423
+ */
424
+ public getActiveConsumers(): string[] {
425
+ if (!this.messageSubscriber) {
426
+ return [];
427
+ }
428
+ return this.messageSubscriber.getActiveConsumers();
429
+ }
430
+
431
+ /**
432
+ * Reset singleton instance (for testing purposes)
433
+ */
434
+ public static resetInstance(): void {
435
+ if (SdkRabbitmq.instance) {
436
+ SdkRabbitmq.instance.disconnect().catch(() => {
437
+ // Ignore errors during reset
438
+ });
439
+ }
440
+ SdkRabbitmq.instance = null;
441
+ SdkRabbitmq.isInitializing = false;
442
+ }
443
+ }