@onlineapps/mq-client-core 1.0.69 → 1.0.71
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 +1 -1
- package/src/BaseClient.js +93 -0
- package/src/transports/rabbitmqClient.js +101 -7
package/package.json
CHANGED
package/src/BaseClient.js
CHANGED
|
@@ -199,6 +199,99 @@ class BaseClient {
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Assert queue existence (delegates to transport).
|
|
204
|
+
* @param {string} queue - Queue name
|
|
205
|
+
* @param {Object} [options]
|
|
206
|
+
* @returns {Promise<Object>}
|
|
207
|
+
*/
|
|
208
|
+
async assertQueue(queue, options = {}) {
|
|
209
|
+
if (!this._connected || !this._transport) {
|
|
210
|
+
throw new ConnectionError('Cannot assertQueue: client is not connected');
|
|
211
|
+
}
|
|
212
|
+
if (typeof this._transport.assertQueue !== 'function') {
|
|
213
|
+
throw new Error('[BaseClient] Transport does not support assertQueue(queue, options)');
|
|
214
|
+
}
|
|
215
|
+
return await this._transport.assertQueue(queue, options);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Check queue existence (delegates to transport).
|
|
220
|
+
* @param {string} queue - Queue name
|
|
221
|
+
* @returns {Promise<Object>}
|
|
222
|
+
*/
|
|
223
|
+
async checkQueue(queue) {
|
|
224
|
+
if (!this._connected || !this._transport) {
|
|
225
|
+
throw new ConnectionError('Cannot checkQueue: client is not connected');
|
|
226
|
+
}
|
|
227
|
+
if (typeof this._transport.checkQueue !== 'function') {
|
|
228
|
+
throw new Error('[BaseClient] Transport does not support checkQueue(queue)');
|
|
229
|
+
}
|
|
230
|
+
return await this._transport.checkQueue(queue);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Purge queue (delegates to transport).
|
|
235
|
+
* @param {string} queue - Queue name
|
|
236
|
+
* @returns {Promise<Object>}
|
|
237
|
+
*/
|
|
238
|
+
async purgeQueue(queue) {
|
|
239
|
+
if (!this._connected || !this._transport) {
|
|
240
|
+
throw new ConnectionError('Cannot purgeQueue: client is not connected');
|
|
241
|
+
}
|
|
242
|
+
if (typeof this._transport.purgeQueue !== 'function') {
|
|
243
|
+
throw new Error('[BaseClient] Transport does not support purgeQueue(queue)');
|
|
244
|
+
}
|
|
245
|
+
return await this._transport.purgeQueue(queue);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Delete queue (delegates to transport).
|
|
250
|
+
* @param {string} queue - Queue name
|
|
251
|
+
* @param {Object} [options]
|
|
252
|
+
* @returns {Promise<Object>}
|
|
253
|
+
*/
|
|
254
|
+
async deleteQueue(queue, options = {}) {
|
|
255
|
+
if (!this._connected || !this._transport) {
|
|
256
|
+
throw new ConnectionError('Cannot deleteQueue: client is not connected');
|
|
257
|
+
}
|
|
258
|
+
if (typeof this._transport.deleteQueue !== 'function') {
|
|
259
|
+
throw new Error('[BaseClient] Transport does not support deleteQueue(queue, options)');
|
|
260
|
+
}
|
|
261
|
+
return await this._transport.deleteQueue(queue, options);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Acknowledge a raw broker message (delegates to transport).
|
|
266
|
+
* @param {Object} msg - Broker message object
|
|
267
|
+
* @returns {Promise<void>}
|
|
268
|
+
*/
|
|
269
|
+
async ack(msg) {
|
|
270
|
+
if (!this._connected || !this._transport) {
|
|
271
|
+
throw new ConnectionError('Cannot ack: client is not connected');
|
|
272
|
+
}
|
|
273
|
+
if (typeof this._transport.ack !== 'function') {
|
|
274
|
+
throw new Error('[BaseClient] Transport does not support ack(msg)');
|
|
275
|
+
}
|
|
276
|
+
return await this._transport.ack(msg);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Negative-acknowledge a raw broker message (delegates to transport).
|
|
281
|
+
* @param {Object} msg - Broker message object
|
|
282
|
+
* @param {Object} [options] - { requeue: boolean }
|
|
283
|
+
* @returns {Promise<void>}
|
|
284
|
+
*/
|
|
285
|
+
async nack(msg, options = {}) {
|
|
286
|
+
if (!this._connected || !this._transport) {
|
|
287
|
+
throw new ConnectionError('Cannot nack: client is not connected');
|
|
288
|
+
}
|
|
289
|
+
if (typeof this._transport.nack !== 'function') {
|
|
290
|
+
throw new Error('[BaseClient] Transport does not support nack(msg, options)');
|
|
291
|
+
}
|
|
292
|
+
return await this._transport.nack(msg, options);
|
|
293
|
+
}
|
|
294
|
+
|
|
202
295
|
/**
|
|
203
296
|
* Acknowledges a RabbitMQ message.
|
|
204
297
|
* @param {Object} msg - RabbitMQ message object.
|
|
@@ -419,6 +419,7 @@ class RabbitMQClient extends EventEmitter {
|
|
|
419
419
|
* @throws {Error} If connection or channel creation fails.
|
|
420
420
|
*/
|
|
421
421
|
async connect() {
|
|
422
|
+
let connectTimeoutTimer = null;
|
|
422
423
|
try {
|
|
423
424
|
const rawTarget = this._config.host || this._config.url;
|
|
424
425
|
const heartbeat = this._config.heartbeat ?? 30;
|
|
@@ -433,9 +434,11 @@ class RabbitMQClient extends EventEmitter {
|
|
|
433
434
|
: [{ ...rawTarget, heartbeat, clientProperties: { connection_name: connectionName } }];
|
|
434
435
|
|
|
435
436
|
const connectPromise = amqp.connect(...connectArgs);
|
|
436
|
-
let connectTimeoutTimer = null;
|
|
437
437
|
const timeoutPromise = new Promise((_, reject) => {
|
|
438
438
|
connectTimeoutTimer = setTimeout(() => reject(new Error('Connection timeout after 10 seconds')), 10000);
|
|
439
|
+
if (connectTimeoutTimer && typeof connectTimeoutTimer.unref === 'function') {
|
|
440
|
+
connectTimeoutTimer.unref();
|
|
441
|
+
}
|
|
439
442
|
});
|
|
440
443
|
console.log('[RabbitMQClient] Starting connection race...');
|
|
441
444
|
this._connection = await Promise.race([connectPromise, timeoutPromise]);
|
|
@@ -537,6 +540,11 @@ class RabbitMQClient extends EventEmitter {
|
|
|
537
540
|
this._connection = null;
|
|
538
541
|
}
|
|
539
542
|
throw err;
|
|
543
|
+
} finally {
|
|
544
|
+
if (connectTimeoutTimer) {
|
|
545
|
+
clearTimeout(connectTimeoutTimer);
|
|
546
|
+
connectTimeoutTimer = null;
|
|
547
|
+
}
|
|
540
548
|
}
|
|
541
549
|
}
|
|
542
550
|
|
|
@@ -1084,6 +1092,92 @@ class RabbitMQClient extends EventEmitter {
|
|
|
1084
1092
|
this._disconnecting = false;
|
|
1085
1093
|
}
|
|
1086
1094
|
|
|
1095
|
+
/**
|
|
1096
|
+
* Ensure a queue exists with correct parameters (TTL/DLQ/etc).
|
|
1097
|
+
* This is required by higher-level routing libraries (e.g. cookbook-router QueueManager).
|
|
1098
|
+
*
|
|
1099
|
+
* @param {string} queue - Queue name
|
|
1100
|
+
* @param {Object} [options]
|
|
1101
|
+
* @returns {Promise<Object>} amqplib assertQueue result
|
|
1102
|
+
*/
|
|
1103
|
+
async assertQueue(queue, options = {}) {
|
|
1104
|
+
await this._ensureQueueChannel();
|
|
1105
|
+
if (!this._queueChannel) {
|
|
1106
|
+
throw new Error('Cannot assertQueue: queue channel is not initialized');
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
let queueOptions = {
|
|
1110
|
+
durable: options.durable !== false,
|
|
1111
|
+
arguments: { ...(options.arguments || {}) }
|
|
1112
|
+
};
|
|
1113
|
+
|
|
1114
|
+
// Prefer central queueConfig (fail-fast queue argument consistency)
|
|
1115
|
+
try {
|
|
1116
|
+
const queueConfig = require('../config/queueConfig');
|
|
1117
|
+
const isInfraQueue = queueConfig ? queueConfig.isInfrastructureQueue(queue) : false;
|
|
1118
|
+
const isBusinessQueue = queueConfig ? queueConfig.isBusinessQueue(queue) : false;
|
|
1119
|
+
|
|
1120
|
+
if (isInfraQueue) {
|
|
1121
|
+
const infraConfig = queueConfig.getInfrastructureQueueConfig(queue);
|
|
1122
|
+
queueOptions = {
|
|
1123
|
+
durable: infraConfig.durable !== false,
|
|
1124
|
+
arguments: { ...infraConfig.arguments }
|
|
1125
|
+
};
|
|
1126
|
+
} else if (isBusinessQueue) {
|
|
1127
|
+
const parsed = queueConfig.parseBusinessQueue(queue);
|
|
1128
|
+
if (parsed) {
|
|
1129
|
+
const businessConfig = queueConfig.getBusinessQueueConfig(parsed.queueType, parsed.serviceName);
|
|
1130
|
+
queueOptions = {
|
|
1131
|
+
durable: businessConfig.durable !== false,
|
|
1132
|
+
arguments: { ...businessConfig.arguments }
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
} catch (err) {
|
|
1137
|
+
// If queueConfig cannot be loaded, keep caller-provided options (explicit).
|
|
1138
|
+
// No fallbacks to "random defaults" here.
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
return await this._queueChannel.assertQueue(queue, queueOptions);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* @param {string} queue - Queue name
|
|
1146
|
+
* @returns {Promise<Object>} amqplib checkQueue result
|
|
1147
|
+
*/
|
|
1148
|
+
async checkQueue(queue) {
|
|
1149
|
+
await this._ensureQueueChannel();
|
|
1150
|
+
if (!this._queueChannel) {
|
|
1151
|
+
throw new Error('Cannot checkQueue: queue channel is not initialized');
|
|
1152
|
+
}
|
|
1153
|
+
return await this._queueChannel.checkQueue(queue);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* @param {string} queue - Queue name
|
|
1158
|
+
* @returns {Promise<Object>} amqplib purgeQueue result
|
|
1159
|
+
*/
|
|
1160
|
+
async purgeQueue(queue) {
|
|
1161
|
+
await this._ensureQueueChannel();
|
|
1162
|
+
if (!this._queueChannel) {
|
|
1163
|
+
throw new Error('Cannot purgeQueue: queue channel is not initialized');
|
|
1164
|
+
}
|
|
1165
|
+
return await this._queueChannel.purgeQueue(queue);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
/**
|
|
1169
|
+
* @param {string} queue - Queue name
|
|
1170
|
+
* @param {Object} [options]
|
|
1171
|
+
* @returns {Promise<Object>} amqplib deleteQueue result
|
|
1172
|
+
*/
|
|
1173
|
+
async deleteQueue(queue, options = {}) {
|
|
1174
|
+
await this._ensureQueueChannel();
|
|
1175
|
+
if (!this._queueChannel) {
|
|
1176
|
+
throw new Error('Cannot deleteQueue: queue channel is not initialized');
|
|
1177
|
+
}
|
|
1178
|
+
return await this._queueChannel.deleteQueue(queue, options);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1087
1181
|
/**
|
|
1088
1182
|
* Publishes a message buffer to the specified queue (or default queue) or exchange.
|
|
1089
1183
|
* Implements systematic retry with exponential backoff for transient failures.
|
|
@@ -2034,11 +2128,11 @@ class RabbitMQClient extends EventEmitter {
|
|
|
2034
2128
|
* @param {Object} msg - RabbitMQ message object.
|
|
2035
2129
|
*/
|
|
2036
2130
|
async ack(msg) {
|
|
2037
|
-
if (!this.
|
|
2038
|
-
throw new Error('Cannot ack: channel is not initialized');
|
|
2131
|
+
if (!this._consumerChannel) {
|
|
2132
|
+
throw new Error('Cannot ack: consumer channel is not initialized');
|
|
2039
2133
|
}
|
|
2040
2134
|
try {
|
|
2041
|
-
this.
|
|
2135
|
+
this._consumerChannel.ack(msg);
|
|
2042
2136
|
} catch (err) {
|
|
2043
2137
|
this.emit('error', err);
|
|
2044
2138
|
throw err;
|
|
@@ -2051,12 +2145,12 @@ class RabbitMQClient extends EventEmitter {
|
|
|
2051
2145
|
* @param {Object} [options] - { requeue: boolean }.
|
|
2052
2146
|
*/
|
|
2053
2147
|
async nack(msg, options = {}) {
|
|
2054
|
-
if (!this.
|
|
2055
|
-
throw new Error('Cannot nack: channel is not initialized');
|
|
2148
|
+
if (!this._consumerChannel) {
|
|
2149
|
+
throw new Error('Cannot nack: consumer channel is not initialized');
|
|
2056
2150
|
}
|
|
2057
2151
|
const requeue = options.requeue !== undefined ? options.requeue : true;
|
|
2058
2152
|
try {
|
|
2059
|
-
this.
|
|
2153
|
+
this._consumerChannel.nack(msg, false, requeue);
|
|
2060
2154
|
} catch (err) {
|
|
2061
2155
|
this.emit('error', err);
|
|
2062
2156
|
throw err;
|