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,411 @@
1
+ // amqplib types are used via ConnectionManager
2
+ import { IResourceCreator, QueueOptions, ExchangeOptions } from '../interfaces/IResource';
3
+ import { ConnectionManager } from './ConnectionManager';
4
+ import { Logger } from './Logger';
5
+ import { IConfiguration } from '../interfaces/IConfiguration';
6
+
7
+ /**
8
+ * ResourceCreator handles automatic creation of exchanges, queues, and bindings
9
+ * Implements caching and existence checking to avoid duplicate creation attempts
10
+ */
11
+ export class ResourceCreator implements IResourceCreator {
12
+ private connectionManager: ConnectionManager;
13
+ private createdExchanges = new Set<string>();
14
+ private createdQueues = new Set<string>();
15
+ private createdBindings = new Set<string>();
16
+ private existingExchanges = new Set<string>();
17
+ private existingQueues = new Set<string>();
18
+ private logger: Logger;
19
+
20
+ constructor(connectionManager: ConnectionManager, config?: IConfiguration['logging']) {
21
+ this.connectionManager = connectionManager;
22
+ this.logger = Logger.createComponentLogger('ResourceCreator', config);
23
+ }
24
+
25
+ /**
26
+ * Check if exchange exists without creating it
27
+ * @param exchange Exchange name
28
+ * @returns Promise<boolean> indicating if exchange exists
29
+ */
30
+ private async checkExchangeExists(exchange: string): Promise<boolean> {
31
+ if (this.existingExchanges.has(exchange)) {
32
+ return true;
33
+ }
34
+
35
+ return this.connectionManager.executeOperation(async () => {
36
+ const connection = this.connectionManager.getConnection();
37
+ if (!connection) {
38
+ throw new Error('No active connection to RabbitMQ');
39
+ }
40
+
41
+ try {
42
+ const channel = await (connection as any).createChannel();
43
+
44
+ // Use checkExchange to verify existence without creating
45
+ await channel.checkExchange(exchange);
46
+ await channel.close();
47
+
48
+ // Cache that exchange exists
49
+ this.existingExchanges.add(exchange);
50
+ return true;
51
+ } catch (error) {
52
+ // Exchange doesn't exist or other error
53
+ return false;
54
+ }
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Check if queue exists without creating it
60
+ * @param queue Queue name
61
+ * @returns Promise<boolean> indicating if queue exists
62
+ */
63
+ private async checkQueueExists(queue: string): Promise<boolean> {
64
+ if (this.existingQueues.has(queue)) {
65
+ return true;
66
+ }
67
+
68
+ return this.connectionManager.executeOperation(async () => {
69
+ const connection = this.connectionManager.getConnection();
70
+ if (!connection) {
71
+ throw new Error('No active connection to RabbitMQ');
72
+ }
73
+
74
+ try {
75
+ const channel = await (connection as any).createChannel();
76
+
77
+ // Use checkQueue to verify existence without creating
78
+ await channel.checkQueue(queue);
79
+ await channel.close();
80
+
81
+ // Cache that queue exists
82
+ this.existingQueues.add(queue);
83
+ return true;
84
+ } catch (error) {
85
+ // Queue doesn't exist or other error
86
+ return false;
87
+ }
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Ensure exchange exists, create if it doesn't
93
+ * @param exchange Exchange name
94
+ * @param type Exchange type (default: 'direct')
95
+ * @param options Exchange options
96
+ */
97
+ public async ensureExchange(
98
+ exchange: string,
99
+ type: string = 'topic',
100
+ options: ExchangeOptions = {}
101
+ ): Promise<void> {
102
+ if (!exchange) {
103
+ throw new Error('Exchange name is required');
104
+ }
105
+
106
+ const exchangeKey = `${exchange}:${type}`;
107
+
108
+ // Check cache first
109
+ if (this.createdExchanges.has(exchangeKey)) {
110
+ return;
111
+ }
112
+
113
+ // Check if exchange already exists
114
+ try {
115
+ const exists = await this.checkExchangeExists(exchange);
116
+ if (exists) {
117
+ this.createdExchanges.add(exchangeKey);
118
+ this.logger.info(`Exchange already exists`, { exchange, type });
119
+ return;
120
+ }
121
+ } catch (error) {
122
+ this.logger.warn(`Could not check exchange existence`, {
123
+ exchange,
124
+ error: error instanceof Error ? error.message : 'Unknown error'
125
+ });
126
+ }
127
+
128
+ return this.connectionManager.executeOperation(async () => {
129
+ const connection = this.connectionManager.getConnection();
130
+ if (!connection) {
131
+ throw new Error('No active connection to RabbitMQ');
132
+ }
133
+
134
+ try {
135
+ const channel = await (connection as any).createChannel();
136
+
137
+ // Set default options
138
+ const exchangeOptions = {
139
+ durable: true,
140
+ autoDelete: false,
141
+ ...options
142
+ };
143
+
144
+ await channel.assertExchange(exchange, type, exchangeOptions);
145
+ await channel.close();
146
+
147
+ // Cache successful creation
148
+ this.createdExchanges.add(exchangeKey);
149
+ this.existingExchanges.add(exchange);
150
+
151
+ this.logger.logOperation('createExchange', `Exchange created successfully`, {
152
+ exchange,
153
+ type,
154
+ options: exchangeOptions
155
+ });
156
+ } catch (error) {
157
+ this.logger.logError(`Failed to ensure exchange`, error as Error, { exchange, type });
158
+ throw error;
159
+ }
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Ensure queue exists, create if it doesn't
165
+ * @param queue Queue name
166
+ * @param options Queue options
167
+ */
168
+ public async ensureQueue(queue: string, options: QueueOptions = {}): Promise<void> {
169
+ if (!queue) {
170
+ throw new Error('Queue name is required');
171
+ }
172
+
173
+ // Check cache first
174
+ if (this.createdQueues.has(queue)) {
175
+ return;
176
+ }
177
+
178
+ // Check if queue already exists
179
+ try {
180
+ const exists = await this.checkQueueExists(queue);
181
+ if (exists) {
182
+ this.createdQueues.add(queue);
183
+ this.logger.info(`Queue already exists`, { queue });
184
+ return;
185
+ }
186
+ } catch (error) {
187
+ this.logger.warn(`Could not check queue existence`, {
188
+ queue,
189
+ error: error instanceof Error ? error.message : 'Unknown error'
190
+ });
191
+ }
192
+
193
+ return this.connectionManager.executeOperation(async () => {
194
+ const connection = this.connectionManager.getConnection();
195
+ if (!connection) {
196
+ throw new Error('No active connection to RabbitMQ');
197
+ }
198
+
199
+ try {
200
+ const channel = await (connection as any).createChannel();
201
+
202
+ // Set default options
203
+ const queueOptions = {
204
+ durable: true,
205
+ exclusive: false,
206
+ autoDelete: false,
207
+ ...options
208
+ };
209
+
210
+ await channel.assertQueue(queue, queueOptions);
211
+ await channel.close();
212
+
213
+ // Cache successful creation
214
+ this.createdQueues.add(queue);
215
+ this.existingQueues.add(queue);
216
+
217
+ this.logger.logOperation('createQueue', `Queue created successfully`, {
218
+ queue,
219
+ options: queueOptions
220
+ });
221
+ } catch (error) {
222
+ this.logger.logError(`Failed to ensure queue`, error as Error, { queue });
223
+ throw error;
224
+ }
225
+ });
226
+ }
227
+
228
+ /**
229
+ * Bind queue to exchange with routing key
230
+ * @param queue Queue name
231
+ * @param exchange Exchange name
232
+ * @param routingKey Routing key for binding
233
+ */
234
+ public async bindQueue(queue: string, exchange: string, routingKey: string): Promise<void> {
235
+ if (!queue) {
236
+ throw new Error('Queue name is required');
237
+ }
238
+ if (!exchange) {
239
+ throw new Error('Exchange name is required');
240
+ }
241
+ if (!routingKey) {
242
+ throw new Error('Routing key is required');
243
+ }
244
+
245
+ const bindingKey = `${queue}:${exchange}:${routingKey}`;
246
+
247
+ // Check cache first
248
+ if (this.createdBindings.has(bindingKey)) {
249
+ return;
250
+ }
251
+
252
+ return this.connectionManager.executeOperation(async () => {
253
+ const connection = this.connectionManager.getConnection();
254
+ if (!connection) {
255
+ throw new Error('No active connection to RabbitMQ');
256
+ }
257
+
258
+ try {
259
+ const channel = await (connection as any).createChannel();
260
+
261
+ await channel.bindQueue(queue, exchange, routingKey);
262
+ await channel.close();
263
+
264
+ // Cache successful binding
265
+ this.createdBindings.add(bindingKey);
266
+
267
+ this.logger.logOperation('bindQueue', `Queue bound to exchange successfully`, {
268
+ queue,
269
+ exchange,
270
+ routingKey
271
+ });
272
+ } catch (error) {
273
+ this.logger.logError(`Failed to bind queue to exchange`, error as Error, {
274
+ queue,
275
+ exchange,
276
+ routingKey
277
+ });
278
+ throw error;
279
+ }
280
+ });
281
+ }
282
+
283
+ /**
284
+ * Unbind queue from exchange with routing key
285
+ * @param queue Queue name
286
+ * @param exchange Exchange name
287
+ * @param routingKey Routing key for unbinding
288
+ */
289
+ public async unbindQueue(queue: string, exchange: string, routingKey: string): Promise<void> {
290
+ if (!queue) {
291
+ throw new Error('Queue name is required');
292
+ }
293
+ if (!exchange) {
294
+ throw new Error('Exchange name is required');
295
+ }
296
+ if (!routingKey) {
297
+ throw new Error('Routing key is required');
298
+ }
299
+
300
+ const bindingKey = `${queue}:${exchange}:${routingKey}`;
301
+
302
+ return this.connectionManager.executeOperation(async () => {
303
+ const connection = this.connectionManager.getConnection();
304
+ if (!connection) {
305
+ throw new Error('No active connection to RabbitMQ');
306
+ }
307
+
308
+ try {
309
+ const channel = await (connection as any).createChannel();
310
+
311
+ await channel.unbindQueue(queue, exchange, routingKey);
312
+ await channel.close();
313
+
314
+ // Remove from cache if it exists
315
+ this.createdBindings.delete(bindingKey);
316
+
317
+ this.logger.logOperation('unbindQueue', `Queue unbound from exchange successfully`, {
318
+ queue,
319
+ exchange,
320
+ routingKey
321
+ });
322
+ } catch (error) {
323
+ this.logger.logError(`Failed to unbind queue from exchange`, error as Error, {
324
+ queue,
325
+ exchange,
326
+ routingKey
327
+ });
328
+ throw error;
329
+ }
330
+ });
331
+ }
332
+
333
+ /**
334
+ * Clear all caches (useful for testing or reset scenarios)
335
+ */
336
+ public clearCache(): void {
337
+ this.createdExchanges.clear();
338
+ this.createdQueues.clear();
339
+ this.createdBindings.clear();
340
+ this.existingExchanges.clear();
341
+ this.existingQueues.clear();
342
+ this.logger.info('ResourceCreator cache cleared');
343
+ }
344
+
345
+ /**
346
+ * Get cache statistics for monitoring
347
+ */
348
+ public getCacheStats(): {
349
+ created: { exchanges: number; queues: number; bindings: number };
350
+ existing: { exchanges: number; queues: number };
351
+ } {
352
+ return {
353
+ created: {
354
+ exchanges: this.createdExchanges.size,
355
+ queues: this.createdQueues.size,
356
+ bindings: this.createdBindings.size
357
+ },
358
+ existing: {
359
+ exchanges: this.existingExchanges.size,
360
+ queues: this.existingQueues.size
361
+ }
362
+ };
363
+ }
364
+
365
+ /**
366
+ * Validate resource name format
367
+ * @param name Resource name to validate
368
+ * @param type Resource type for error messages
369
+ */
370
+ private validateResourceName(name: string, type: string): void {
371
+ if (!name || typeof name !== 'string') {
372
+ throw new Error(`${type} name must be a non-empty string`);
373
+ }
374
+
375
+ if (name.length > 255) {
376
+ throw new Error(`${type} name cannot exceed 255 characters`);
377
+ }
378
+
379
+ // RabbitMQ naming conventions
380
+ const invalidChars = /[^a-zA-Z0-9._-]/;
381
+ if (invalidChars.test(name)) {
382
+ throw new Error(`${type} name contains invalid characters. Only alphanumeric, dots, hyphens, and underscores are allowed`);
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Enhanced ensure exchange with validation
388
+ */
389
+ public async ensureExchangeWithValidation(
390
+ exchange: string,
391
+ type: string = 'topic',
392
+ options: ExchangeOptions = {}
393
+ ): Promise<void> {
394
+ this.validateResourceName(exchange, 'Exchange');
395
+
396
+ const validTypes = ['direct', 'topic', 'fanout', 'headers'];
397
+ if (!validTypes.includes(type)) {
398
+ throw new Error(`Invalid exchange type '${type}'. Valid types: ${validTypes.join(', ')}`);
399
+ }
400
+
401
+ return this.ensureExchange(exchange, type, options);
402
+ }
403
+
404
+ /**
405
+ * Enhanced ensure queue with validation
406
+ */
407
+ public async ensureQueueWithValidation(queue: string, options: QueueOptions = {}): Promise<void> {
408
+ this.validateResourceName(queue, 'Queue');
409
+ return this.ensureQueue(queue, options);
410
+ }
411
+ }