@droidsolutions-oss/amqp-ts 3.0.4 → 4.0.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.
Files changed (48) hide show
  1. package/README.md +186 -45
  2. package/lib/AmqpProperties.js +1 -2
  3. package/lib/Binding.d.ts +36 -6
  4. package/lib/Binding.js +244 -75
  5. package/lib/Binding.js.map +1 -1
  6. package/lib/Connection/Connection.d.ts +87 -46
  7. package/lib/Connection/Connection.js +360 -181
  8. package/lib/Connection/Connection.js.map +1 -1
  9. package/lib/Connection/ConnectionMetrics.js +1 -2
  10. package/lib/Connection/RecoveryOptions.d.ts +6 -0
  11. package/lib/Connection/RecoveryOptions.js +2 -0
  12. package/lib/Connection/RecoveryOptions.js.map +1 -0
  13. package/lib/Connection/Topology.d.ts +1 -1
  14. package/lib/Connection/Topology.js +1 -2
  15. package/lib/Deferred.d.ts +18 -0
  16. package/lib/Deferred.js +33 -0
  17. package/lib/Deferred.js.map +1 -0
  18. package/lib/Exchange/DeclarationOptions.js +1 -2
  19. package/lib/Exchange/Exchange.d.ts +38 -17
  20. package/lib/Exchange/Exchange.js +204 -159
  21. package/lib/Exchange/Exchange.js.map +1 -1
  22. package/lib/Exchange/ExchangeType.js +1 -2
  23. package/lib/Exchange/InitializeResult.js +1 -2
  24. package/lib/LoggerFactory.js +1 -5
  25. package/lib/LoggerFactory.js.map +1 -1
  26. package/lib/Message.d.ts +11 -10
  27. package/lib/Message.js +66 -37
  28. package/lib/Message.js.map +1 -1
  29. package/lib/Queue/DeclarationOptions.js +1 -2
  30. package/lib/Queue/DeleteResult.js +1 -2
  31. package/lib/Queue/InitializeResult.js +1 -2
  32. package/lib/Queue/Queue.d.ts +87 -26
  33. package/lib/Queue/Queue.js +440 -207
  34. package/lib/Queue/Queue.js.map +1 -1
  35. package/lib/Queue/StartConsumerResult.js +1 -2
  36. package/lib/amqp-ts.d.ts +0 -1
  37. package/lib/amqp-ts.js +2 -7
  38. package/lib/amqp-ts.js.map +1 -1
  39. package/lib/errorMessage.d.ts +1 -0
  40. package/lib/errorMessage.js +18 -0
  41. package/lib/errorMessage.js.map +1 -0
  42. package/lib/index.d.ts +19 -14
  43. package/lib/index.js +7 -13
  44. package/lib/index.js.map +1 -1
  45. package/package.json +38 -60
  46. package/lib/Connection/ReconnectStrategy.d.ts +0 -4
  47. package/lib/Connection/ReconnectStrategy.js +0 -3
  48. package/lib/Connection/ReconnectStrategy.js.map +0 -1
@@ -1,151 +1,365 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Connection = void 0;
4
- const AmqpLib = require("amqplib/callback_api");
5
- const events_1 = require("events");
6
- const Exchange_1 = require("../Exchange/Exchange");
7
- const LoggerFactory_1 = require("../LoggerFactory");
8
- const Queue_1 = require("../Queue/Queue");
9
- class Connection extends events_1.EventEmitter {
1
+ import { connect } from "amqplib";
2
+ import { EventEmitter } from "node:events";
3
+ import { Binding } from "../Binding.js";
4
+ import { Deferred } from "../Deferred.js";
5
+ import { Exchange } from "../Exchange/Exchange.js";
6
+ import { errorMessage } from "../errorMessage.js";
7
+ import { EmtpyLogger } from "../LoggerFactory.js";
8
+ import { Queue } from "../Queue/Queue.js";
9
+ export class Connection extends EventEmitter {
10
+ loggerFactory;
11
+ /**
12
+ * Resolves once the connection is established and the declared topology was set up. It is replaced by a pending
13
+ * promise when the connection is lost and resolves again after the next successful reconnect. It rejects when
14
+ * recovery gives up or the connection is closed before it was established.
15
+ *
16
+ * It only reflects the connection: an exchange, queue, binding or consumer that fails on its own does not keep it
17
+ * from resolving. Use {@link completeConfiguration} to wait for the topology.
18
+ */
19
+ initialized;
20
+ isConnected = false;
21
+ /**
22
+ * The current underlying amqplib model, `undefined` while disconnected.
23
+ * @internal
24
+ */
25
+ _model;
26
+ /**
27
+ * Identifies the current underlying model. Changes on each successful connect, so entities can tell whether they
28
+ * are initialized on the current connection.
29
+ * @internal
30
+ */
31
+ _generation = 0;
32
+ /** @internal */
33
+ _exchanges;
34
+ /** @internal */
35
+ _queues;
36
+ /** @internal */
37
+ _bindings;
38
+ _deferred;
39
+ _recovering;
40
+ /** Counter for connect attempts that reach `setup`; each of them gets its own generation. */
41
+ _lastGeneration = 0;
42
+ /** The model `setup` is currently running for, and its generation. */
43
+ _setupModel;
44
+ _setupGeneration = 0;
45
+ _closedModels = new WeakSet();
46
+ _connectedBefore = false;
47
+ _isClosing = false;
48
+ _closing;
49
+ /** Why recovery is over, set by {@link close} or when recovery gave up. Nothing is initialized after that. */
50
+ _stopError;
51
+ /** Why exchanges, queues and bindings failed on their own, until the same one is declared again. */
52
+ _failures = new Map();
53
+ log;
54
+ _sentMessagesCounter = 0;
55
+ _receivedMessagesCounter = 0;
10
56
  /**
11
57
  *
12
58
  * @param url The url to RabbitMQ
13
- * @param socketOptions Socket options are passed to {@link AmqpLib.connect}.
14
- * @param reconnectStrategy Options specifying the reconnect strategy.
59
+ * @param socketOptions Socket options are passed to amqplib's `connect`.
60
+ * @param recovery Options for the automatic reconnect, passed to amqplib's connection recovery. By default it retries
61
+ * forever with exponential backoff.
15
62
  * @param loggerFactory A factory function that returns a logger.
16
63
  */
17
- constructor(url = "amqp://localhost", socketOptions = {}, reconnectStrategy = { retries: 0, interval: 1500 }, loggerFactory) {
64
+ constructor(url = "amqp://localhost", socketOptions = {}, recovery = {}, loggerFactory) {
18
65
  super();
19
66
  this.loggerFactory = loggerFactory;
20
- this.isConnected = false;
21
- this.connectedBefore = false;
22
- this._rebuilding = false;
23
- this._isClosing = false;
24
- this._sentMessagesCounter = 0;
25
- this._receivedMessagesCounter = 0;
26
67
  if (!this.loggerFactory) {
27
- this.loggerFactory = (_, __) => new LoggerFactory_1.EmtpyLogger();
68
+ this.loggerFactory = (_, __) => new EmtpyLogger();
28
69
  }
29
70
  this.log = this.loggerFactory(this.constructor, { module: "amqp-ts" });
30
- this.url = url;
31
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
32
- this.socketOptions = socketOptions;
33
- this.reconnectStrategy = reconnectStrategy;
34
71
  this._exchanges = {};
35
72
  this._queues = {};
36
73
  this._bindings = {};
37
- this.rebuildConnection();
74
+ this._deferred = new Deferred();
75
+ this.initialized = this._deferred.promise;
76
+ // waitForConnect: false resolves right away and defers the first attempt, so the listeners are attached in time
77
+ this._recovering = connect(url, {
78
+ ...socketOptions,
79
+ recovery: { ...recovery, waitForConnect: false, setup: (model) => this._setup(model) },
80
+ });
81
+ this._recovering.then((recovering) => this._listen(recovering),
82
+ /* istanbul ignore next */ (err) => this._giveUp(err));
38
83
  }
39
- tryToConnect(thisConnection, retry, callback) {
40
- AmqpLib.connect(thisConnection.url, thisConnection.socketOptions, (err, connection) => {
41
- /* istanbul ignore if */
42
- if (err) {
43
- thisConnection.isConnected = false;
44
- // only do every retry once, amqplib can return multiple connection errors for one connection request (error?)
45
- if (retry <= this._retry) {
46
- //amqpts_log.log("warn" , "Double retry " + retry + ", skipping.", {module: "amqp-ts"});
47
- return;
48
- }
49
- this.log.warn({ err }, "Connection failed.");
50
- this._retry = retry;
51
- if (thisConnection.reconnectStrategy.retries === 0 || thisConnection.reconnectStrategy.retries > retry) {
52
- this.log.warn("Connection retry %d in %d ms", retry + 1, thisConnection.reconnectStrategy.interval);
53
- thisConnection.emit("trying_connect");
54
- setTimeout(
55
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
56
- thisConnection.tryToConnect.bind(this), thisConnection.reconnectStrategy.interval, thisConnection, retry + 1, callback);
57
- }
58
- else {
59
- //no reconnect strategy, or retries exhausted, so return the error
60
- this.log.warn("Connection failed, exiting: No connection retries left (retry %d).", retry);
61
- callback(err);
84
+ close() {
85
+ if (this._closing === undefined) {
86
+ this._isClosing = true;
87
+ // buffered sends and anyone awaiting initialized must not wait for a connection that never comes
88
+ this._stop(new Error("Connection closed"));
89
+ this._closing = this._close();
90
+ }
91
+ return this._closing;
92
+ }
93
+ /**
94
+ * A new deferred for an entity's promise. Once the connection was closed or recovery gave up nothing would
95
+ * ever settle it, so it is rejected right away.
96
+ * @internal
97
+ */
98
+ _createDeferred() {
99
+ const deferred = new Deferred();
100
+ if (this._stopError !== undefined) {
101
+ deferred.reject(this._stopError);
102
+ }
103
+ return deferred;
104
+ }
105
+ /**
106
+ * An exchange, queue or binding failed on its own, not because the connection was lost: it is no longer
107
+ * declared and its {@link initialized} is rejected.
108
+ *
109
+ * The bindings of a failed exchange or queue can never be created any more, so they are rejected and removed as
110
+ * well. The failure is emitted as `error_connection`, see {@link _reportFailure}.
111
+ * @internal
112
+ */
113
+ _onEntityFailed(entity, err) {
114
+ if (!(entity instanceof Binding)) {
115
+ for (const [id, binding] of Object.entries(this._bindings)) {
116
+ if (binding._source === entity || binding._destination === entity) {
117
+ delete this._bindings[id];
118
+ binding._rejectPending(err);
62
119
  }
63
120
  }
64
- else {
65
- const restart = (err) => {
66
- this.log.debug({ err }, "Connection error occurred.");
67
- connection.removeListener("error", restart);
68
- //connection.removeListener("end", restart); // not sure this is needed
69
- thisConnection._rebuildAll(err); //try to rebuild the topology when the connection unexpectedly closes
70
- };
71
- const onClose = () => {
72
- connection.removeListener("close", onClose);
73
- if (!this._isClosing) {
74
- thisConnection.emit("lost_connection");
75
- restart(new Error("Connection closed by remote host"));
76
- }
77
- };
78
- connection.on("error", restart);
79
- connection.on("close", onClose);
80
- //connection.on("end", restart); // not sure this is needed
81
- thisConnection.connection = connection;
82
- thisConnection.isConnected = true;
83
- callback(null);
121
+ }
122
+ this._failures.set(Connection._failureKey(entity), err);
123
+ this._reportFailure(err);
124
+ }
125
+ /**
126
+ * An exchange, queue or binding is declared (again): a failure of an earlier one with the same name no longer
127
+ * counts for {@link completeConfiguration}.
128
+ * @internal
129
+ */
130
+ _forgetFailure(entity) {
131
+ this._failures.delete(Connection._failureKey(entity));
132
+ }
133
+ static _failureKey(entity) {
134
+ if (entity instanceof Binding) {
135
+ return `binding ${entity._id}`;
136
+ }
137
+ return `${entity instanceof Queue ? "queue" : "exchange"} ${entity.name}`;
138
+ }
139
+ /**
140
+ * Emits `error_connection` for an exchange, queue, binding or consumer that failed on its own, whether it was
141
+ * declared for the first time or again during recovery. Nobody has to await the entity's promise, for example after
142
+ * a `declareQueue` nobody awaits or during recovery, so this is the one signal that always arrives.
143
+ * @internal
144
+ */
145
+ _reportFailure(err) {
146
+ this._emitSafely("error_connection", err);
147
+ }
148
+ /**
149
+ * The model entities are initialized on right now: the current one, or while `setup` runs the one it runs for.
150
+ * `undefined` while disconnected.
151
+ * @internal
152
+ */
153
+ get _activeModel() {
154
+ return this._model ?? this._setupModel;
155
+ }
156
+ /**
157
+ * Whether the connection was closed or recovery gave up, so nothing is initialized any more.
158
+ * @internal
159
+ */
160
+ get _isStopped() {
161
+ return this._isClosing || this._stopError !== undefined;
162
+ }
163
+ /**
164
+ * Whether the given model has closed.
165
+ * @internal
166
+ */
167
+ _isModelClosed(model) {
168
+ return this._closedModels.has(model);
169
+ }
170
+ /**
171
+ * Whether a channel of the given model may be re-created after it closed: the model is open, and it is
172
+ * either the current one or the one `setup` is running for.
173
+ * @internal
174
+ */
175
+ _isModelUsable(model) {
176
+ return !this._isClosing && !this._closedModels.has(model) && (model === this._model || model === this._setupModel);
177
+ }
178
+ async _close() {
179
+ const recovering = await this._recovering.catch(() => undefined);
180
+ if (recovering) {
181
+ await recovering.close();
182
+ }
183
+ this._model = undefined;
184
+ this.isConnected = false;
185
+ this.log.info("AMQP Connection closed.");
186
+ this._emitSafely("close_connection");
187
+ }
188
+ /**
189
+ * Recovery is over, because of {@link close} or because it gave up: rejects every promise that is still pending,
190
+ * the connection's and the entities', since nothing settles them any more.
191
+ */
192
+ _stop(err) {
193
+ this._stopError ??= err;
194
+ if (!this._deferred.settled) {
195
+ this._deferred.reject(err);
196
+ }
197
+ for (const exchange of Object.values(this._exchanges)) {
198
+ exchange._rejectPending(err);
199
+ exchange._notifyChanged();
200
+ }
201
+ for (const queue of Object.values(this._queues)) {
202
+ queue._rejectPending(err);
203
+ queue._notifyChanged();
204
+ }
205
+ for (const binding of Object.values(this._bindings)) {
206
+ binding._rejectPending(err);
207
+ }
208
+ }
209
+ /**
210
+ * Emits an event to the user's listeners. Most events are emitted from within amqplib's recovery, where a throwing
211
+ * listener would stop the reconnect from being scheduled, or from a promise chain, where it would be an unhandled
212
+ * rejection. So a listener's error is logged instead, and the listeners after it still get the event. The order is
213
+ * that of `emit`, and `once` listeners remove themselves as usual.
214
+ */
215
+ _emitSafely(event, ...args) {
216
+ for (const listener of this.rawListeners(event)) {
217
+ try {
218
+ listener.apply(this, args);
84
219
  }
220
+ catch (err) {
221
+ this.log.error({ err }, "A listener for '%s' threw: %s", event, errorMessage(err));
222
+ }
223
+ }
224
+ }
225
+ _listen(recovering) {
226
+ recovering.on("connect", (model) => this._onConnect(model));
227
+ recovering.on("disconnect", (err) => this._onDisconnect(err));
228
+ recovering.on("connect-failed", (err) => {
229
+ this.log.warn({ err }, "Connection failed.");
230
+ });
231
+ recovering.on("reconnect-scheduled", ({ attempt, delay }) => {
232
+ this.log.warn("Connection retry %d in %d ms", attempt, delay);
233
+ this._emitSafely("trying_connect");
234
+ });
235
+ recovering.on("reconnect-failed", (err) => this._giveUp(err));
236
+ recovering.on("error", (err) => {
237
+ this.log.warn({ err }, "Connection error: %s", err.message);
238
+ });
239
+ recovering.on("handler-error", (err, event) => {
240
+ this.log.error({ err }, "A listener for '%s' threw: %s", event, err.message);
241
+ });
242
+ recovering.on("blocked", (reason) => {
243
+ this.log.warn("Connection blocked by the broker: %s", reason);
244
+ });
245
+ recovering.on("unblocked", () => {
246
+ this.log.info("Connection unblocked by the broker.");
85
247
  });
86
248
  }
87
- _rebuildAll(err) {
88
- this.log.warn({ err }, "Connection error: %s", err.message);
89
- this.log.debug("Rebuilding connection NOW.");
90
- this.rebuildConnection();
91
- //re initialize exchanges, queues and bindings if they exist
92
- for (const exchangeId in this._exchanges) {
93
- const exchange = this._exchanges[exchangeId];
94
- this.log.debug("Re-initialize Exchange '%s'.", exchange.name);
95
- exchange._initialize();
249
+ /**
250
+ * Runs after every successful (re)connect, before amqplib installs the model: rebuilds the topology on fresh
251
+ * channels. It only throws when the model itself closed meanwhile, which makes amqplib schedule another reconnect.
252
+ */
253
+ async _setup(model) {
254
+ const generation = ++this._lastGeneration;
255
+ this._setupModel = model;
256
+ this._setupGeneration = generation;
257
+ // until amqplib installs the model it has no listeners on it, and an "error" without a listener would throw
258
+ model.on("error", (err) => {
259
+ this.log.debug({ err }, "Connection error during setup: %s", err.message);
260
+ });
261
+ model.on("close", () => {
262
+ this._closedModels.add(model);
263
+ });
264
+ this.log.debug("Initializing the topology on a new connection.");
265
+ await this._declareEntities(model, generation);
266
+ if (this._closedModels.has(model)) {
267
+ throw new Error("Connection closed while the topology was initialized");
96
268
  }
97
- for (const queueId in this._queues) {
98
- const queue = this._queues[queueId];
99
- const consumer = queue._consumer;
100
- this.log.debug("Re-initialize queue '%s'.", queue.name);
101
- queue._initialize();
102
- if (consumer) {
103
- this.log.debug("Re-initialize consumer for queue '%s'.", queue.name);
104
- queue._initializeConsumer();
269
+ }
270
+ /**
271
+ * Initializes every entity that is not initialized on the given generation yet: exchanges, then queues, then
272
+ * bindings, then queue consumers. Entity failures are handled by the entities themselves.
273
+ *
274
+ * A binding whose source exchange belongs to another connection waits for that connection, so it is not awaited:
275
+ * an outage of the other connection must not hold up this one's recovery.
276
+ */
277
+ async _declareEntities(model, generation) {
278
+ const settle = async (promises) => {
279
+ await Promise.allSettled(promises);
280
+ };
281
+ await settle(Object.values(this._exchanges).map((exchange) => exchange._initialize(model, generation)));
282
+ await settle(Object.values(this._queues).map((queue) => queue._initialize(model, generation)));
283
+ await settle(Object.values(this._bindings).flatMap((binding) => {
284
+ const initializing = binding._initialize(model, generation);
285
+ if (binding._isCrossConnection) {
286
+ initializing.catch(() => { });
287
+ return [];
288
+ }
289
+ return [initializing];
290
+ }));
291
+ await settle(Object.values(this._queues).map((queue) => queue._initializeConsumer()));
292
+ }
293
+ _onConnect(model) {
294
+ const generation = this._setupGeneration;
295
+ this._setupModel = undefined;
296
+ this._model = model;
297
+ this._generation = generation;
298
+ this.isConnected = true;
299
+ // entities declared while setup was running were not part of it
300
+ void this._declareEntities(model, generation).then(() => {
301
+ if (this._isClosing || this._model !== model) {
302
+ return;
303
+ }
304
+ this._deferred.resolve();
305
+ if (this._connectedBefore) {
306
+ this.log.info("AMQP Connection re-established");
307
+ this._emitSafely("re_established_connection");
308
+ }
309
+ else {
310
+ this._connectedBefore = true;
311
+ this.log.info("AQMP Connection established.");
312
+ this._emitSafely("open_connection");
105
313
  }
314
+ });
315
+ }
316
+ _onDisconnect(err) {
317
+ this._model = undefined;
318
+ this.isConnected = false;
319
+ for (const exchange of Object.values(this._exchanges)) {
320
+ exchange._invalidate();
106
321
  }
107
- for (const bindingId in this._bindings) {
108
- const binding = this._bindings[bindingId];
109
- this.log.debug("Re-initialize binding from '%s' to '%s'.", binding._source.name, binding._destination.name);
110
- binding._initialize();
322
+ for (const queue of Object.values(this._queues)) {
323
+ queue._invalidate();
324
+ }
325
+ for (const binding of Object.values(this._bindings)) {
326
+ binding._invalidate();
327
+ }
328
+ this._invalidate();
329
+ if (!this._isClosing) {
330
+ this.log.warn({ err }, "Connection lost: %s", err.message);
331
+ this._emitSafely("lost_connection");
111
332
  }
112
- return new Promise((resolve, reject) => {
113
- this.completeConfiguration().then(() => {
114
- this.log.debug("Rebuild success.");
115
- resolve(null);
116
- },
117
- /* istanbul ignore next */ (rejectReason) => {
118
- this.log.debug("Rebuild failed.");
119
- reject(rejectReason);
120
- });
121
- });
122
333
  }
123
- close() {
124
- this._isClosing = true;
125
- return new Promise((resolve, reject) => {
126
- this.initialized.then(() => {
127
- this.connection.close((err) => {
128
- /* istanbul ignore if */
129
- if (err) {
130
- reject(err);
131
- }
132
- else {
133
- this.isConnected = false;
134
- this.emit("close_connection");
135
- resolve(null);
136
- }
137
- });
138
- });
139
- });
334
+ /** Recovery gave up: no further reconnect is attempted. */
335
+ _giveUp(err) {
336
+ this._model = undefined;
337
+ this.isConnected = false;
338
+ this._invalidate();
339
+ this._stop(err);
340
+ this.log.warn({ err }, "Connection failed, giving up: %s", errorMessage(err));
341
+ this._emitSafely("error_connection", err);
342
+ }
343
+ /** Replaces a settled {@link initialized} with a pending one. */
344
+ _invalidate() {
345
+ if (this._deferred.settled) {
346
+ this._deferred = new Deferred();
347
+ this.initialized = this._deferred.promise;
348
+ }
140
349
  }
141
350
  /**
142
- * Make sure the whole defined connection topology is configured. Goes through each exchange, queue and binding and
143
- * waits until all of them are initialized.
351
+ * Waits until the whole declared topology is initialized: every exchange, queue and binding, and the consumer of
352
+ * every queue with an activated consumer. Use it to gate the start of an application, since {@link initialized}
353
+ * only reflects the connection.
144
354
  *
145
- * @return A promise that fulfills after all defined exchanges, queues and bindings are initialized.
355
+ * It rejects when one of them failed on its own, at the first connect or later. A failed exchange, queue or binding
356
+ * counts until one with the same name is declared again (or {@link deleteConfiguration} is called), a refused
357
+ * consumer until it is registered on the next reconnect. While the connection is down it waits for the reconnect.
358
+ *
359
+ * @return A promise that fulfills after all defined exchanges, queues, bindings and consumers are initialized.
146
360
  */
147
361
  completeConfiguration() {
148
- const promises = [];
362
+ const promises = [...this._failures.values()].map((err) => Promise.reject(err));
149
363
  for (const exchangeId in this._exchanges) {
150
364
  const exchange = this._exchanges[exchangeId];
151
365
  promises.push(exchange.initialized);
@@ -164,27 +378,31 @@ class Connection extends events_1.EventEmitter {
164
378
  return Promise.all(promises);
165
379
  }
166
380
  /**
167
- * Delete the whole defined connection topology:
168
- * return promise that fulfills after all defined exchanges, queues and bindings have been removed
381
+ * Deletes the whole defined topology in phases: first the bindings, then the queues together with their consumers,
382
+ * then the exchanges. Deleting a queue or an exchange deletes its bindings as well, so running the phases one after
383
+ * the other unbinds every binding only once. A phase that fails does not stop the next one.
384
+ *
385
+ * @return A promise that fulfills after all defined exchanges, queues and bindings have been removed, and rejects
386
+ * with the first failure otherwise.
169
387
  */
170
- deleteConfiguration() {
171
- const promises = [];
172
- for (const bindingId in this._bindings) {
173
- const binding = this._bindings[bindingId];
174
- promises.push(binding.delete());
175
- }
176
- for (const queueId in this._queues) {
177
- const queue = this._queues[queueId];
178
- if (queue._consumerInitialized !== undefined) {
179
- promises.push(queue.stopConsumer());
388
+ async deleteConfiguration() {
389
+ // what failed is not declared any more, so there is nothing to delete
390
+ this._failures.clear();
391
+ const failures = [];
392
+ const phase = async (deletions) => {
393
+ for (const result of await Promise.allSettled(deletions)) {
394
+ if (result.status === "rejected") {
395
+ failures.push(result.reason);
396
+ }
180
397
  }
181
- promises.push(queue.delete());
182
- }
183
- for (const exchangeId in this._exchanges) {
184
- const exchange = this._exchanges[exchangeId];
185
- promises.push(exchange.delete());
398
+ };
399
+ await phase(Object.values(this._bindings).map((binding) => binding.delete()));
400
+ // deleting a queue stops its consumer first
401
+ await phase(Object.values(this._queues).map((queue) => queue.delete()));
402
+ await phase(Object.values(this._exchanges).map((exchange) => exchange.delete()));
403
+ if (failures.length > 0) {
404
+ throw failures[0];
186
405
  }
187
- return Promise.all(promises);
188
406
  }
189
407
  /**
190
408
  * Declares a new exchange. Dependent on the options it auto creates the exchange if it does not already exists.
@@ -197,7 +415,7 @@ class Connection extends events_1.EventEmitter {
197
415
  declareExchange(name, type, options) {
198
416
  let exchange = this._exchanges[name];
199
417
  if (exchange === undefined) {
200
- exchange = new Exchange_1.Exchange(this, name, type, options);
418
+ exchange = new Exchange(this, name, type, options);
201
419
  }
202
420
  return exchange;
203
421
  }
@@ -224,7 +442,7 @@ class Connection extends events_1.EventEmitter {
224
442
  declareQueue(name, options) {
225
443
  let queue = this._queues[name];
226
444
  if (queue === undefined) {
227
- queue = new Queue_1.Queue(this, name, options);
445
+ queue = new Queue(this, name, options);
228
446
  }
229
447
  return queue;
230
448
  }
@@ -273,8 +491,9 @@ class Connection extends events_1.EventEmitter {
273
491
  }
274
492
  return Promise.all(promises);
275
493
  }
494
+ /** The current underlying amqplib model, `undefined` while disconnected. */
276
495
  get getConnection() {
277
- return this.connection;
496
+ return this._model;
278
497
  }
279
498
  /**
280
499
  * Returns an object with metrics about the connection.
@@ -286,50 +505,11 @@ class Connection extends events_1.EventEmitter {
286
505
  sentMessagesCounter: this._sentMessagesCounter,
287
506
  };
288
507
  }
289
- rebuildConnection() {
290
- if (this._rebuilding) {
291
- // only one rebuild process can be active at any time
292
- this.log.debug("AMQP Connection rebuild already in progress, joining active rebuild attempt.");
293
- return this.initialized;
294
- }
295
- this._retry = -1;
296
- this._rebuilding = true;
297
- this._isClosing = false;
298
- // rebuild the connection
299
- this.initialized = new Promise((resolve, reject) => {
300
- this.tryToConnect(this, 0, (err) => {
301
- /* istanbul ignore if */
302
- if (err) {
303
- this._rebuilding = false;
304
- reject(err);
305
- }
306
- else {
307
- this._rebuilding = false;
308
- if (this.connectedBefore) {
309
- this.log.info("AMQP Connection re-established");
310
- this.emit("re_established_connection");
311
- }
312
- else {
313
- this.log.info("AQMP Connection established.");
314
- this.emit("open_connection");
315
- this.connectedBefore = true;
316
- }
317
- resolve(null);
318
- }
319
- });
320
- });
321
- /* istanbul ignore next */
322
- this.initialized.catch((err) => {
323
- this.log.warn({ err }, "Error creating connection!");
324
- this.emit("error_connection", err);
325
- //throw (err);
326
- });
327
- return this.initialized;
328
- }
329
508
  /**
330
509
  * Increments an internal counter.
331
510
  * @param counter The counter to increase.
332
511
  * @param value The increment, default is 1.
512
+ * @internal
333
513
  */
334
514
  _increaseCounter(counter, value = 1) {
335
515
  switch (counter) {
@@ -342,5 +522,4 @@ class Connection extends events_1.EventEmitter {
342
522
  }
343
523
  }
344
524
  }
345
- exports.Connection = Connection;
346
525
  //# sourceMappingURL=Connection.js.map