amqp-connection-manager 3.5.1 → 3.8.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.
package/README.md CHANGED
@@ -2,13 +2,8 @@
2
2
 
3
3
  [![NPM version](https://badge.fury.io/js/amqp-connection-manager.svg)](https://npmjs.org/package/amqp-connection-manager)
4
4
  ![Build Status](https://github.com/jwalton/node-amqp-connection-manager/workflows/GitHub%20CI/badge.svg)
5
- [![Coverage Status](https://coveralls.io/repos/jwalton/node-amqp-connection-manager/badge.svg?branch=master&service=github)](https://coveralls.io/github/jwalton/node-amqp-connection-manager?branch=master)
6
5
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
7
6
 
8
- [![Dependency Status](https://david-dm.org/jwalton/node-amqp-connection-manager.svg)](https://david-dm.org/jwalton/node-amqp-connection-manager)
9
- [![devDependency Status](https://david-dm.org/jwalton/node-amqp-connection-manager/dev-status.svg)](https://david-dm.org/jwalton/node-amqp-connection-manager#info=devDependencies)
10
- [![peerDependency Status](https://david-dm.org/jwalton/node-amqp-connection-manager/peer-status.svg)](https://david-dm.org/jwalton/node-amqp-connection-manager#info=peerDependencies)
11
-
12
7
  Connection management for amqplib. This is a wrapper around [amqplib](http://www.squaremobius.net/amqp.node/) which provides automatic reconnects.
13
8
 
14
9
  ## Features
@@ -137,8 +132,10 @@ Options:
137
132
  Note that `this` inside the setup function will the returned ChannelWrapper.
138
133
  The ChannelWrapper has a special `context` member you can use to store
139
134
  arbitrary data in.
140
- - `options.json` if true, then ChannelWrapper assumes all messages passed to `publish()` and `sendToQueue()`
135
+ - `options.json` - if true, then ChannelWrapper assumes all messages passed to `publish()` and `sendToQueue()`
141
136
  are plain JSON objects. These will be encoded automatically before being sent.
137
+ - `options.confirm` - if true (default), the created channel will be a ConfirmChannel
138
+ - `options.publishTimeout` - a default timeout for messages published to this channel.
142
139
 
143
140
  ### AmqpConnectionManager#isConnected()
144
141
 
@@ -183,6 +180,12 @@ callback) which resolves when the message is confirmed to have been delivered to
183
180
  either the broker refuses the message, or if `close()` is called on the ChannelWrapper before the message can be
184
181
  delivered.
185
182
 
183
+ Both of these functions take an additional option when passing options:
184
+
185
+ - `timeout` - If specified, if a messages is not acked by the amqp broker within the specified number of milliseconds,
186
+ the message will be rejected. Note that the message _may_ still end up getting delivered after the timeout, as we
187
+ have no way to cancel the in-flight request.
188
+
186
189
  ### ChannelWrapper#ack and ChannelWrapper#nack
187
190
 
188
191
  These are just aliases for calling `ack()` and `nack()` on the underlying channel. They do nothing if the underlying
@@ -72,7 +72,41 @@ class AmqpConnectionManager extends events_1.EventEmitter {
72
72
  // There will be one listener per channel, and there could be a lot of channels, so disable warnings from node.
73
73
  this.setMaxListeners(0);
74
74
  this._findServers = options.findServers || (() => Promise.resolve(urls));
75
+ }
76
+ /**
77
+ * Start the connect retries and await the first connect result. Even if the initial connect fails or timeouts, the
78
+ * reconnect attempts will continue in the background.
79
+ * @param [options={}] -
80
+ * @param [options.timeout] - Time to wait for initial connect
81
+ */
82
+ async connect({ timeout } = {}) {
75
83
  this._connect();
84
+ let reject;
85
+ const onDisconnect = ({ err }) => {
86
+ // Ignore disconnects caused by dead servers etc., but throw on operational errors like bad credentials.
87
+ if (err.isOperational) {
88
+ reject(err);
89
+ }
90
+ };
91
+ try {
92
+ await Promise.race([
93
+ (0, events_1.once)(this, 'connect'),
94
+ new Promise((_resolve, innerReject) => {
95
+ reject = innerReject;
96
+ this.on('disconnect', onDisconnect);
97
+ }),
98
+ ...(timeout
99
+ ? [
100
+ (0, helpers_js_1.wait)(timeout).promise.then(() => {
101
+ throw new Error('amqp-connection-manager: connect timeout');
102
+ }),
103
+ ]
104
+ : []),
105
+ ]);
106
+ }
107
+ finally {
108
+ this.removeListener('disconnect', onDisconnect);
109
+ }
76
110
  }
77
111
  // `options` here are any options that can be passed to ChannelWrapper.
78
112
  createChannel(options = {}) {
@@ -221,7 +255,7 @@ class AmqpConnectionManager extends events_1.EventEmitter {
221
255
  this._currentConnection = undefined;
222
256
  this._disconnectSent = true;
223
257
  this.emit('disconnect', { err });
224
- const handle = helpers_js_1.wait(this.reconnectTimeInSeconds * 1000);
258
+ const handle = (0, helpers_js_1.wait)(this.reconnectTimeInSeconds * 1000);
225
259
  this._cancelRetriesHandler = handle.cancel;
226
260
  handle.promise
227
261
  .then(() => this._connect())
@@ -245,10 +279,10 @@ class AmqpConnectionManager extends events_1.EventEmitter {
245
279
  this._connectPromise = undefined;
246
280
  let handle;
247
281
  if (err.name === 'OperationalError' && err.message === 'connect ETIMEDOUT') {
248
- handle = helpers_js_1.wait(0);
282
+ handle = (0, helpers_js_1.wait)(0);
249
283
  }
250
284
  else {
251
- handle = helpers_js_1.wait(this.reconnectTimeInSeconds * 1000);
285
+ handle = (0, helpers_js_1.wait)(this.reconnectTimeInSeconds * 1000);
252
286
  }
253
287
  this._cancelRetriesHandler = handle.cancel;
254
288
  return handle.promise.then(() => this._connect());
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const events_1 = require("events");
7
7
  const promise_breaker_1 = __importDefault(require("promise-breaker"));
8
+ const MAX_MESSAGES_PER_BATCH = 1000;
8
9
  const IRRECOVERABLE_ERRORS = [
9
10
  403,
10
11
  404,
@@ -46,7 +47,7 @@ class ChannelWrapper extends events_1.EventEmitter {
46
47
  *
47
48
  */
48
49
  constructor(connectionManager, options = {}) {
49
- var _a;
50
+ var _a, _b;
50
51
  super();
51
52
  /** If we're in the process of creating a channel, this is a Promise which
52
53
  * will resolve when the channel is set up. Otherwise, this is `null`.
@@ -56,6 +57,12 @@ class ChannelWrapper extends events_1.EventEmitter {
56
57
  this._messages = [];
57
58
  /** Oublished, but not yet confirmed messages. */
58
59
  this._unconfirmedMessages = [];
60
+ /** Consumers which will be reconnected on channel errors etc. */
61
+ this._consumers = [];
62
+ /**
63
+ * True to create a ConfirmChannel. False to create a regular Channel.
64
+ */
65
+ this._confirm = true;
59
66
  /**
60
67
  * True if the "worker" is busy sending messages. False if we need to
61
68
  * start the worker to get stuff done.
@@ -67,13 +74,20 @@ class ChannelWrapper extends events_1.EventEmitter {
67
74
  * workers ever do wake up, they'll know to stop working.
68
75
  */
69
76
  this._workerNumber = 0;
77
+ /**
78
+ * True if the underlying channel has room for more messages.
79
+ */
80
+ this._channelHasRoom = true;
70
81
  this._onConnect = this._onConnect.bind(this);
71
82
  this._onDisconnect = this._onDisconnect.bind(this);
72
83
  this._connectionManager = connectionManager;
84
+ this._confirm = (_a = options.confirm) !== null && _a !== void 0 ? _a : true;
73
85
  this.name = options.name;
74
- this._json = (_a = options.json) !== null && _a !== void 0 ? _a : false;
86
+ this._publishTimeout = options.publishTimeout;
87
+ this._json = (_b = options.json) !== null && _b !== void 0 ? _b : false;
75
88
  // Array of setup functions to call.
76
89
  this._setups = [];
90
+ this._consumers = [];
77
91
  if (options.setup) {
78
92
  this._setups.push(options.setup);
79
93
  }
@@ -172,15 +186,17 @@ class ChannelWrapper extends events_1.EventEmitter {
172
186
  */
173
187
  publish(exchange, routingKey, content, options, done) {
174
188
  return promise_breaker_1.default.addCallback(done, new Promise((resolve, reject) => {
175
- this._messages.push({
189
+ const { timeout, ...opts } = options || {};
190
+ this._enqueueMessage({
176
191
  type: 'publish',
177
192
  exchange,
178
193
  routingKey,
179
- content,
180
- options,
194
+ content: this._getEncodedMessage(content),
181
195
  resolve,
182
196
  reject,
183
- });
197
+ options: opts,
198
+ isTimedout: false,
199
+ }, timeout || this._publishTimeout);
184
200
  this._startWorker();
185
201
  }));
186
202
  }
@@ -194,25 +210,55 @@ class ChannelWrapper extends events_1.EventEmitter {
194
210
  * `message` here should be a JSON-able object.
195
211
  */
196
212
  sendToQueue(queue, content, options, done) {
213
+ const encodedContent = this._getEncodedMessage(content);
197
214
  return promise_breaker_1.default.addCallback(done, new Promise((resolve, reject) => {
198
- this._messages.push({
215
+ const { timeout, ...opts } = options || {};
216
+ this._enqueueMessage({
199
217
  type: 'sendToQueue',
200
218
  queue,
201
- content,
202
- options,
219
+ content: encodedContent,
203
220
  resolve,
204
221
  reject,
205
- });
206
- return this._startWorker();
222
+ options: opts,
223
+ isTimedout: false,
224
+ }, timeout || this._publishTimeout);
225
+ this._startWorker();
207
226
  }));
208
227
  }
228
+ _enqueueMessage(message, timeout) {
229
+ if (timeout) {
230
+ message.timeout = setTimeout(() => {
231
+ let idx = this._messages.indexOf(message);
232
+ if (idx !== -1) {
233
+ this._messages.splice(idx, 1);
234
+ }
235
+ else {
236
+ idx = this._unconfirmedMessages.indexOf(message);
237
+ if (idx !== -1) {
238
+ this._unconfirmedMessages.splice(idx, 1);
239
+ }
240
+ }
241
+ message.isTimedout = true;
242
+ message.reject(new Error('timeout'));
243
+ }, timeout);
244
+ }
245
+ this._messages.push(message);
246
+ }
209
247
  // Called whenever we connect to the broker.
210
248
  async _onConnect({ connection }) {
211
249
  this._irrecoverableCode = undefined;
212
250
  try {
213
- const channel = await connection.createConfirmChannel();
251
+ let channel;
252
+ if (this._confirm) {
253
+ channel = await connection.createConfirmChannel();
254
+ }
255
+ else {
256
+ channel = await connection.createChannel();
257
+ }
214
258
  this._channel = channel;
259
+ this._channelHasRoom = true;
215
260
  channel.on('close', () => this._onChannelClose(channel));
261
+ channel.on('drain', () => this._onChannelDrain());
216
262
  this._settingUp = Promise.all(this._setups.map((setupFn) =>
217
263
  // TODO: Use a timeout here to guard against setupFns that never resolve?
218
264
  promise_breaker_1.default.call(setupFn, this, channel).catch((err) => {
@@ -221,7 +267,11 @@ class ChannelWrapper extends events_1.EventEmitter {
221
267
  return;
222
268
  }
223
269
  this.emit('error', err, { name: this.name });
224
- }))).then(() => {
270
+ })))
271
+ .then(() => {
272
+ return Promise.all(this._consumers.map((c) => this._reconnectConsumer(c)));
273
+ })
274
+ .then(() => {
225
275
  this._settingUp = undefined;
226
276
  });
227
277
  await this._settingUp;
@@ -229,13 +279,6 @@ class ChannelWrapper extends events_1.EventEmitter {
229
279
  // Can happen if channel closes while we're setting up.
230
280
  return;
231
281
  }
232
- if (this._unconfirmedMessages.length > 0) {
233
- // requeue any messages that were left unconfirmed when connection was lost
234
- let message;
235
- while ((message = this._unconfirmedMessages.shift())) {
236
- this._messages.push(message);
237
- }
238
- }
239
282
  // Since we just connected, publish any queued messages
240
283
  this._startWorker();
241
284
  this.emit('connect');
@@ -253,6 +296,11 @@ class ChannelWrapper extends events_1.EventEmitter {
253
296
  }
254
297
  // Wait for another reconnect to create a new channel.
255
298
  }
299
+ /** Called whenever the channel drains. */
300
+ _onChannelDrain() {
301
+ this._channelHasRoom = true;
302
+ this._startWorker();
303
+ }
256
304
  // Called whenever we disconnect from the AMQP server.
257
305
  _onDisconnect(ex) {
258
306
  this._irrecoverableCode = ex.err instanceof Error ? ex.err.code : undefined;
@@ -275,11 +323,21 @@ class ChannelWrapper extends events_1.EventEmitter {
275
323
  this._working = false;
276
324
  if (this._messages.length !== 0) {
277
325
  // Reject any unsent messages.
278
- this._messages.forEach((message) => message.reject(new Error('Channel closed')));
326
+ this._messages.forEach((message) => {
327
+ if (message.timeout) {
328
+ clearTimeout(message.timeout);
329
+ }
330
+ message.reject(new Error('Channel closed'));
331
+ });
279
332
  }
280
333
  if (this._unconfirmedMessages.length !== 0) {
281
334
  // Reject any unconfirmed messages.
282
- this._unconfirmedMessages.forEach((message) => message.reject(new Error('Channel closed')));
335
+ this._unconfirmedMessages.forEach((message) => {
336
+ if (message.timeout) {
337
+ clearTimeout(message.timeout);
338
+ }
339
+ message.reject(new Error('Channel closed'));
340
+ });
283
341
  }
284
342
  this._connectionManager.removeListener('connect', this._onConnect);
285
343
  this._connectionManager.removeListener('disconnect', this._onDisconnect);
@@ -290,7 +348,7 @@ class ChannelWrapper extends events_1.EventEmitter {
290
348
  });
291
349
  }
292
350
  _shouldPublish() {
293
- return this._messages.length > 0 && !this._settingUp && !!this._channel;
351
+ return (this._messages.length > 0 && !this._settingUp && !!this._channel && this._channelHasRoom);
294
352
  }
295
353
  // Start publishing queued messages, if there isn't already a worker doing this.
296
354
  _startWorker() {
@@ -304,6 +362,44 @@ class ChannelWrapper extends events_1.EventEmitter {
304
362
  _canWaitReconnection() {
305
363
  return !this._irrecoverableCode || !IRRECOVERABLE_ERRORS.includes(this._irrecoverableCode);
306
364
  }
365
+ _messageResolved(message, result) {
366
+ removeUnconfirmedMessage(this._unconfirmedMessages, message);
367
+ message.resolve(result);
368
+ }
369
+ _messageRejected(message, err) {
370
+ if (!this._channel && this._canWaitReconnection()) {
371
+ // Tried to write to a closed channel. Leave the message in the queue and we'll try again when
372
+ // we reconnect.
373
+ removeUnconfirmedMessage(this._unconfirmedMessages, message);
374
+ this._messages.push(message);
375
+ }
376
+ else {
377
+ // Something went wrong trying to send this message - could be JSON.stringify failed, could be
378
+ // the broker rejected the message. Either way, reject it back
379
+ removeUnconfirmedMessage(this._unconfirmedMessages, message);
380
+ message.reject(err);
381
+ }
382
+ }
383
+ _getEncodedMessage(content) {
384
+ let encodedMessage;
385
+ if (this._json) {
386
+ encodedMessage = Buffer.from(JSON.stringify(content));
387
+ }
388
+ else if (typeof content === 'string') {
389
+ encodedMessage = Buffer.from(content);
390
+ }
391
+ else if (content instanceof Buffer) {
392
+ encodedMessage = content;
393
+ }
394
+ else if (typeof content === 'object' && typeof content.toString === 'function') {
395
+ encodedMessage = Buffer.from(content.toString());
396
+ }
397
+ else {
398
+ console.warn('amqp-connection-manager: Sending JSON message, but json option not speicifed');
399
+ encodedMessage = Buffer.from(JSON.stringify(content));
400
+ }
401
+ return encodedMessage;
402
+ }
307
403
  _publishQueuedMessages(workerNumber) {
308
404
  const channel = this._channel;
309
405
  if (!channel ||
@@ -314,90 +410,154 @@ class ChannelWrapper extends events_1.EventEmitter {
314
410
  this._working = false;
315
411
  return;
316
412
  }
317
- const message = this._messages.shift();
318
- if (message) {
319
- this._unconfirmedMessages.push(message);
320
- Promise.resolve()
321
- .then(() => {
322
- let encodedMessage;
323
- if (this._json) {
324
- encodedMessage = Buffer.from(JSON.stringify(message.content));
325
- }
326
- else if (typeof message.content === 'string') {
327
- encodedMessage = Buffer.from(message.content);
328
- }
329
- else if (message.content instanceof Buffer) {
330
- encodedMessage = message.content;
331
- }
332
- else if (typeof message.content === 'object' &&
333
- typeof message.content.toString === 'function') {
334
- encodedMessage = Buffer.from(message.content.toString());
335
- }
336
- else {
337
- throw new Error('Invalid message content');
413
+ try {
414
+ // Send messages in batches of 1000 - don't want to starve the event loop.
415
+ let sendsLeft = MAX_MESSAGES_PER_BATCH;
416
+ while (this._channelHasRoom && this._messages.length > 0 && sendsLeft > 0) {
417
+ sendsLeft--;
418
+ const message = this._messages.shift();
419
+ if (!message) {
420
+ break;
338
421
  }
339
- let result = true;
340
- const sendPromise = (() => {
341
- switch (message.type) {
342
- case 'publish':
343
- return new Promise(function (resolve, reject) {
344
- result = channel.publish(message.exchange, message.routingKey, encodedMessage, message.options, (err) => {
345
- if (err) {
346
- reject(err);
347
- }
348
- else {
349
- resolve(result);
350
- }
351
- });
422
+ let thisCanSend = true;
423
+ switch (message.type) {
424
+ case 'publish': {
425
+ if (this._confirm) {
426
+ this._unconfirmedMessages.push(message);
427
+ thisCanSend = this._channelHasRoom = channel.publish(message.exchange, message.routingKey, message.content, message.options, (err) => {
428
+ if (message.isTimedout) {
429
+ return;
430
+ }
431
+ if (message.timeout) {
432
+ clearTimeout(message.timeout);
433
+ }
434
+ if (err) {
435
+ this._messageRejected(message, err);
436
+ }
437
+ else {
438
+ this._messageResolved(message, thisCanSend);
439
+ }
352
440
  });
353
- case 'sendToQueue':
354
- return new Promise(function (resolve, reject) {
355
- result = channel.sendToQueue(message.queue, encodedMessage, message.options, (err) => {
356
- if (err) {
357
- reject(err);
358
- }
359
- else {
360
- resolve(result);
361
- }
362
- });
441
+ }
442
+ else {
443
+ if (message.timeout) {
444
+ clearTimeout(message.timeout);
445
+ }
446
+ thisCanSend = this._channelHasRoom = channel.publish(message.exchange, message.routingKey, message.content, message.options);
447
+ message.resolve(thisCanSend);
448
+ }
449
+ break;
450
+ }
451
+ case 'sendToQueue': {
452
+ if (this._confirm) {
453
+ this._unconfirmedMessages.push(message);
454
+ thisCanSend = this._channelHasRoom = channel.sendToQueue(message.queue, message.content, message.options, (err) => {
455
+ if (message.isTimedout) {
456
+ return;
457
+ }
458
+ if (message.timeout) {
459
+ clearTimeout(message.timeout);
460
+ }
461
+ if (err) {
462
+ this._messageRejected(message, err);
463
+ }
464
+ else {
465
+ this._messageResolved(message, thisCanSend);
466
+ }
363
467
  });
364
- /* istanbul ignore next */
365
- default:
366
- throw new Error(`Unhandled message type ${message.type}`);
468
+ }
469
+ else {
470
+ if (message.timeout) {
471
+ clearTimeout(message.timeout);
472
+ }
473
+ thisCanSend = this._channelHasRoom = channel.sendToQueue(message.queue, message.content, message.options);
474
+ message.resolve(thisCanSend);
475
+ }
476
+ break;
367
477
  }
368
- })();
369
- if (result) {
370
- this._publishQueuedMessages(workerNumber);
478
+ /* istanbul ignore next */
479
+ default:
480
+ throw new Error(`Unhandled message type ${message.type}`);
371
481
  }
372
- else {
373
- channel.once('drain', () => this._publishQueuedMessages(workerNumber));
374
- }
375
- return sendPromise;
376
- })
377
- .then((result) => {
378
- removeUnconfirmedMessage(this._unconfirmedMessages, message);
379
- message.resolve(result);
380
- }, (err) => {
381
- if (!this._channel && this._canWaitReconnection()) {
382
- // Tried to write to a closed channel. Leave the message in the queue and we'll try again when
383
- // we reconnect.
384
- removeUnconfirmedMessage(this._unconfirmedMessages, message);
385
- this._messages.push(message);
386
- }
387
- else {
388
- // Something went wrong trying to send this message - could be JSON.stringify failed, could be
389
- // the broker rejected the message. Either way, reject it back
390
- removeUnconfirmedMessage(this._unconfirmedMessages, message);
391
- message.reject(err);
392
- }
393
- })
394
- .catch(
395
- /* istanbul ignore next */ (err) => {
396
- this.emit('error', err);
397
- this._working = false;
398
- });
482
+ }
483
+ // If we didn't send all the messages, send some more...
484
+ if (this._channelHasRoom && this._messages.length > 0) {
485
+ setImmediate(() => this._publishQueuedMessages(workerNumber));
486
+ }
487
+ this._working = false;
488
+ /* istanbul ignore next */
489
+ }
490
+ catch (err) {
491
+ this._working = false;
492
+ this.emit('error', err);
399
493
  }
400
494
  }
495
+ /**
496
+ * Setup a consumer
497
+ * This consumer will be reconnected on cancellation and channel errors.
498
+ */
499
+ async consume(queue, onMessage, options = {}) {
500
+ const consumer = {
501
+ consumerTag: null,
502
+ queue,
503
+ onMessage,
504
+ options,
505
+ };
506
+ this._consumers.push(consumer);
507
+ await this._consume(consumer);
508
+ }
509
+ async _consume(consumer) {
510
+ if (!this._channel) {
511
+ return;
512
+ }
513
+ const { prefetch, ...options } = consumer.options;
514
+ if (typeof prefetch === 'number') {
515
+ this._channel.prefetch(prefetch, false);
516
+ }
517
+ const { consumerTag } = await this._channel.consume(consumer.queue, (msg) => {
518
+ if (!msg) {
519
+ consumer.consumerTag = null;
520
+ this._reconnectConsumer(consumer).catch((err) => {
521
+ if (err.isOperational && err.message.includes('BasicConsume; 404')) {
522
+ // Ignore errors caused by queue not declared. In
523
+ // those cases the connection will reconnect and
524
+ // then consumers reestablished. The full reconnect
525
+ // might be avoided if we assert the queue again
526
+ // before starting to consume.
527
+ return;
528
+ }
529
+ throw err;
530
+ });
531
+ return;
532
+ }
533
+ consumer.onMessage(msg);
534
+ }, options);
535
+ consumer.consumerTag = consumerTag;
536
+ }
537
+ async _reconnectConsumer(consumer) {
538
+ if (!this._consumers.includes(consumer)) {
539
+ // Intentionally canceled
540
+ return;
541
+ }
542
+ await this._consume(consumer);
543
+ }
544
+ /**
545
+ * Cancel all consumers
546
+ */
547
+ async cancelAll() {
548
+ const consumers = this._consumers;
549
+ this._consumers = [];
550
+ if (!this._channel) {
551
+ return;
552
+ }
553
+ const channel = this._channel;
554
+ await Promise.all(consumers.reduce((acc, consumer) => {
555
+ if (consumer.consumerTag) {
556
+ acc.push(channel.cancel(consumer.consumerTag));
557
+ }
558
+ return acc;
559
+ }, []));
560
+ }
401
561
  /** Send an `ack` to the underlying channel. */
402
562
  ack(message, allUpTo) {
403
563
  this._channel && this._channel.ack(message, allUpTo);
package/dist/cjs/index.js CHANGED
@@ -3,10 +3,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.connect = void 0;
6
+ exports.AmqpConnectionManagerClass = exports.connect = void 0;
7
7
  const AmqpConnectionManager_js_1 = __importDefault(require("./AmqpConnectionManager.js"));
8
+ exports.AmqpConnectionManagerClass = AmqpConnectionManager_js_1.default;
8
9
  function connect(urls, options) {
9
- return new AmqpConnectionManager_js_1.default(urls, options);
10
+ const conn = new AmqpConnectionManager_js_1.default(urls, options);
11
+ conn.connect().catch(() => {
12
+ /* noop */
13
+ });
14
+ return conn;
10
15
  }
11
16
  exports.connect = connect;
12
17
  const amqp = { connect };
@@ -59,6 +59,7 @@ export interface IAmqpConnectionManager {
59
59
  addListener(event: 'disconnect', listener: (arg: {
60
60
  err: Error;
61
61
  }) => void): this;
62
+ listeners(eventName: string | symbol): Function[];
62
63
  on(event: string, listener: (...args: any[]) => void): this;
63
64
  on(event: 'connect', listener: ConnectListener): this;
64
65
  on(event: 'blocked', listener: (arg: {
@@ -96,13 +97,17 @@ export interface IAmqpConnectionManager {
96
97
  err: Error;
97
98
  }) => void): this;
98
99
  removeListener(event: string, listener: (...args: any[]) => void): this;
100
+ connect(options?: {
101
+ timeout?: number;
102
+ }): Promise<void>;
103
+ reconnect(): void;
99
104
  createChannel(options?: CreateChannelOpts): ChannelWrapper;
100
105
  close(): Promise<void>;
101
106
  isConnected(): boolean;
102
107
  /** The current connection. */
103
- get connection(): Connection | undefined;
108
+ readonly connection: Connection | undefined;
104
109
  /** Returns the number of registered channels. */
105
- get channelCount(): number;
110
+ readonly channelCount: number;
106
111
  }
107
112
  export default class AmqpConnectionManager extends EventEmitter implements IAmqpConnectionManager {
108
113
  private _channels;
@@ -148,6 +153,15 @@ export default class AmqpConnectionManager extends EventEmitter implements IAmqp
148
153
  * Note that if this is supplied, then `urls` is ignored.
149
154
  */
150
155
  constructor(urls: ConnectionUrl | ConnectionUrl[] | undefined | null, options?: AmqpConnectionManagerOptions);
156
+ /**
157
+ * Start the connect retries and await the first connect result. Even if the initial connect fails or timeouts, the
158
+ * reconnect attempts will continue in the background.
159
+ * @param [options={}] -
160
+ * @param [options.timeout] - Time to wait for initial connect
161
+ */
162
+ connect({ timeout }?: {
163
+ timeout?: number;
164
+ }): Promise<void>;
151
165
  createChannel(options?: CreateChannelOpts): ChannelWrapper;
152
166
  close(): Promise<void>;
153
167
  isConnected(): boolean;