@onlineapps/mq-client-core 1.0.32 → 1.0.33

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onlineapps/mq-client-core",
3
- "version": "1.0.32",
3
+ "version": "1.0.33",
4
4
  "description": "Core MQ client library for RabbitMQ - shared by infrastructure services and connectors",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -94,11 +94,20 @@ class RabbitMQClient extends EventEmitter {
94
94
 
95
95
  // Use ConfirmChannel to enable publisher confirms for publish operations
96
96
  this._channel = await this._connection.createConfirmChannel();
97
- this._channel.on('error', (err) => this.emit('error', err));
97
+ // Enable publisher confirms explicitly
98
+ this._channel.on('error', (err) => {
99
+ console.error('[RabbitMQClient] [mq-client-core] Publisher channel error:', err.message);
100
+ this.emit('error', err);
101
+ });
98
102
  this._channel.on('close', () => {
103
+ console.warn('[RabbitMQClient] [mq-client-core] Publisher channel closed');
99
104
  // Emit a channel close error
100
105
  this.emit('error', new Error('RabbitMQ channel closed unexpectedly'));
101
106
  });
107
+ // Set up publisher confirms callback
108
+ this._channel.on('drain', () => {
109
+ console.log('[RabbitMQClient] [mq-client-core] Publisher channel drained');
110
+ });
102
111
 
103
112
  // Create a separate regular channel for queue operations (assertQueue, checkQueue)
104
113
  // This avoids RPC reply queue issues with ConfirmChannel.assertQueue()
@@ -187,8 +196,13 @@ class RabbitMQClient extends EventEmitter {
187
196
  async publish(queue, buffer, options = {}) {
188
197
  // Check channel state before publish
189
198
  if (!this._channel || this._channel.closed) {
190
- throw new Error('Cannot publish: channel is not initialized or closed');
199
+ const errorMsg = `Cannot publish to queue "${queue}": channel is not initialized or closed (channel: ${!!this._channel}, closed: ${this._channel?.closed})`;
200
+ console.error('[RabbitMQClient] [mq-client-core] [PUBLISH]', errorMsg);
201
+ throw new Error(errorMsg);
191
202
  }
203
+
204
+ // Log publish attempt for debugging
205
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] Attempting to publish to queue "${queue}" (channel open: ${!this._channel.closed})`);
192
206
 
193
207
  const exchange = this._config.exchange || '';
194
208
  const routingKey = options.routingKey || queue;
@@ -254,23 +268,90 @@ class RabbitMQClient extends EventEmitter {
254
268
  // Publish to queue using ConfirmChannel (for publisher confirms)
255
269
  // Check channel state again before sendToQueue
256
270
  if (this._channel.closed) {
257
- throw new Error('Cannot publish: channel closed during operation');
271
+ throw new Error(`Cannot publish to queue "${queue}": channel closed during operation (after queue check)`);
272
+ }
273
+
274
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] Sending message to queue "${queue}" (size: ${buffer.length} bytes)`);
275
+
276
+ // Use callback-based confirmation for more reliable error handling
277
+ const confirmPromise = new Promise((resolve, reject) => {
278
+ // Set up timeout
279
+ const timeout = setTimeout(() => {
280
+ reject(new Error(`Publisher confirm timeout after 5 seconds for queue "${queue}"`));
281
+ }, 5000);
282
+
283
+ // Send message with callback
284
+ const sent = this._channel.sendToQueue(queue, buffer, { persistent, headers }, (err, ok) => {
285
+ clearTimeout(timeout);
286
+ if (err) {
287
+ console.error(`[RabbitMQClient] [mq-client-core] [PUBLISH] Send callback error for queue "${queue}":`, err.message);
288
+ reject(err);
289
+ } else {
290
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] ✓ Message confirmed for queue "${queue}"`);
291
+ resolve();
292
+ }
293
+ });
294
+
295
+ // If sendToQueue returns false, channel is full - wait for drain
296
+ if (!sent) {
297
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] Channel full, waiting for drain for queue "${queue}"`);
298
+ this._channel.once('drain', () => {
299
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] Channel drained for queue "${queue}"`);
300
+ });
301
+ }
302
+ });
303
+
304
+ try {
305
+ await confirmPromise;
306
+ } catch (confirmErr) {
307
+ // Check if channel was closed during confirmation
308
+ if (this._channel.closed) {
309
+ const errorMsg = `Publisher confirm failed for queue "${queue}": channel closed during confirmation. Original error: ${confirmErr.message}`;
310
+ console.error('[RabbitMQClient] [mq-client-core] [PUBLISH]', errorMsg);
311
+ throw new Error(errorMsg);
312
+ }
313
+ throw confirmErr;
258
314
  }
259
- this._channel.sendToQueue(queue, buffer, { persistent, headers });
260
315
  } else {
261
316
  // If exchange is specified, assert exchange and publish to it
262
317
  if (this._channel.closed) {
263
318
  throw new Error('Cannot publish: channel closed during operation');
264
319
  }
265
320
  await this._channel.assertExchange(exchange, 'direct', { durable: this._config.durable });
266
- this._channel.publish(exchange, routingKey, buffer, { persistent, headers });
321
+
322
+ // Use callback-based confirmation for exchange publish
323
+ const confirmPromise = new Promise((resolve, reject) => {
324
+ const timeout = setTimeout(() => {
325
+ reject(new Error(`Publisher confirm timeout after 5 seconds for exchange "${exchange}"`));
326
+ }, 5000);
327
+
328
+ const sent = this._channel.publish(exchange, routingKey, buffer, { persistent, headers }, (err, ok) => {
329
+ clearTimeout(timeout);
330
+ if (err) {
331
+ console.error(`[RabbitMQClient] [mq-client-core] [PUBLISH] Exchange publish callback error:`, err.message);
332
+ reject(err);
333
+ } else {
334
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] ✓ Exchange publish confirmed`);
335
+ resolve();
336
+ }
337
+ });
338
+
339
+ if (!sent) {
340
+ this._channel.once('drain', () => {
341
+ console.log(`[RabbitMQClient] [mq-client-core] [PUBLISH] Channel drained for exchange "${exchange}"`);
342
+ });
343
+ }
344
+ });
345
+
346
+ try {
347
+ await confirmPromise;
348
+ } catch (confirmErr) {
349
+ if (this._channel.closed) {
350
+ throw new Error(`Publisher confirm failed for exchange "${exchange}": channel closed during confirmation. Original error: ${confirmErr.message}`);
351
+ }
352
+ throw confirmErr;
353
+ }
267
354
  }
268
- // Wait for confirmation (with timeout to prevent hanging)
269
- const confirmPromise = this._channel.waitForConfirms();
270
- const timeoutPromise = new Promise((_, reject) => {
271
- setTimeout(() => reject(new Error('Publisher confirm timeout after 5 seconds')), 5000);
272
- });
273
- await Promise.race([confirmPromise, timeoutPromise]);
274
355
  } catch (err) {
275
356
  // If channel was closed, mark it for recreation
276
357
  if (err.message && (err.message.includes('Channel closed') || err.message.includes('channel is closed') || this._channel?.closed)) {