@relaybus/rabbitmq 0.3.0 → 0.3.1

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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { RelayBusError, NonRetryableError, assertEventType, systemClock, ConfigurationError, toErrorInfo, computeRetryDelay, isRetryableError, createRetryPolicy, toMilliseconds } from '@relaybus/core';
2
- import { createCloudEventReaders, mediaTypeOf, isJsonObject, createJsonEnvelopeFormat, schedule, writeStructuredCloudEvent, assertPositiveInteger } from '@relaybus/core/internal';
1
+ import { RelayBusError, NonRetryableError, assertEventType, ConfigurationError, systemClock, toErrorInfo, computeRetryDelay, isRetryableError, createRetryPolicy } from '@relaybus/core';
2
+ import { createCloudEventReaders, DecodedEventReader, mediaTypeOf, isJsonObject, observeRejection, createJsonEnvelopeFormat, SubscribingTransport, writeStructuredCloudEvent, schedule, NonEmptyText, PositiveInteger, Milliseconds } from '@relaybus/core/internal';
3
3
  export { fromHeader, fromPayloadField } from '@relaybus/core/internal';
4
4
 
5
5
  // src/errors.ts
@@ -31,6 +31,99 @@ var RabbitMqUnencodableEventError = class extends NonRetryableError {
31
31
  name = "RabbitMqUnencodableEventError";
32
32
  code = "RELAYBUS_RABBITMQ_UNENCODABLE_EVENT";
33
33
  };
34
+ var NACK_MESSAGE = "message nacked";
35
+ var ConfirmedPublishing = class {
36
+ #transportName;
37
+ #publishTimeoutMs;
38
+ constructor(transportName, publishTimeoutMs) {
39
+ this.#transportName = transportName;
40
+ this.#publishTimeoutMs = publishTimeoutMs;
41
+ }
42
+ async publish(publisher, input) {
43
+ const { exchange, routingKey, content, options, mandatory } = input;
44
+ const eventId = options.messageId ?? "unknown";
45
+ if (publisher === void 0) {
46
+ throw new RabbitMqUnavailableError(
47
+ `[RabbitMqTransport](publish) Not connected to the broker | transport=${this.#transportName} destination=${routingKey}`
48
+ );
49
+ }
50
+ const returnKey = unroutableKey(options.messageId, routingKey);
51
+ const target = { eventId, destination: routingKey };
52
+ await new Promise((resolve, reject) => {
53
+ const cancelTimeout = this.#startTimeout(target, reject);
54
+ const pending = { ...target, publisher, returnKey, cancelTimeout, resolve, reject };
55
+ try {
56
+ publisher.channel.publish(
57
+ exchange,
58
+ routingKey,
59
+ content,
60
+ { ...options, mandatory },
61
+ (error) => this.#confirmed(pending, error)
62
+ );
63
+ } catch (error) {
64
+ cancelTimeout();
65
+ reject(this.#toPublishError(error, target));
66
+ }
67
+ });
68
+ }
69
+ #startTimeout({ eventId, destination }, reject) {
70
+ return schedule(systemClock, {
71
+ run: () => {
72
+ reject(
73
+ new RabbitMqUnavailableError(
74
+ `[RabbitMqTransport](publish) No broker confirm within ${this.#publishTimeoutMs}ms | transport=${this.#transportName} eventId=${eventId} destination=${destination}`
75
+ )
76
+ );
77
+ },
78
+ delayMs: this.#publishTimeoutMs
79
+ });
80
+ }
81
+ /** The broker's confirm (or nack) of one message arrived. */
82
+ #confirmed(pending, error) {
83
+ pending.cancelTimeout();
84
+ const returned = takeUnroutable(pending.publisher.unroutable, pending.returnKey);
85
+ if (error) {
86
+ pending.reject(this.#toPublishError(error, pending));
87
+ } else if (returned) {
88
+ pending.reject(
89
+ new UnroutableMessageError(
90
+ `[RabbitMqTransport](publish) No queue is bound to the routing key | transport=${this.#transportName} eventId=${pending.eventId} destination=${pending.destination}`
91
+ )
92
+ );
93
+ } else {
94
+ pending.resolve();
95
+ }
96
+ }
97
+ #toPublishError(error, { eventId, destination }) {
98
+ const { message } = toErrorInfo(error);
99
+ const detail = `transport=${this.#transportName} eventId=${eventId} destination=${destination} error=${message}`;
100
+ return message === NACK_MESSAGE ? new RabbitMqPublishRejectedError(
101
+ `[RabbitMqTransport](publish) Broker nacked the message | ${detail}`,
102
+ { cause: error }
103
+ ) : new RabbitMqUnavailableError(`[RabbitMqTransport](publish) Publish failed | ${detail}`, {
104
+ cause: error
105
+ });
106
+ }
107
+ };
108
+ function unroutableKey(messageId, routingKey) {
109
+ return `${String(messageId)}
110
+ ${routingKey}`;
111
+ }
112
+ function recordUnroutable(unroutable, key) {
113
+ unroutable.set(key, (unroutable.get(key) ?? 0) + 1);
114
+ }
115
+ function takeUnroutable(unroutable, key) {
116
+ const count = unroutable.get(key);
117
+ if (count === void 0) {
118
+ return false;
119
+ }
120
+ if (count === 1) {
121
+ unroutable.delete(key);
122
+ } else {
123
+ unroutable.set(key, count - 1);
124
+ }
125
+ return true;
126
+ }
34
127
  function abortableSleep(milliseconds, signal) {
35
128
  if (signal.aborted) {
36
129
  return Promise.resolve();
@@ -277,35 +370,41 @@ function visitObject(value, {
277
370
  }
278
371
  ancestors.add(value);
279
372
  if (Array.isArray(value)) {
280
- for (let index = 0; index < value.length; index += 1) {
281
- if (!(index in value)) {
282
- throw refuse(eventId, {
283
- path: `${path}[${index}]`,
284
- reason: "is an array hole, which JSON turns into null"
285
- });
286
- }
287
- visit(value[index], { path: `${path}[${index}]`, ancestors, eventId });
288
- }
373
+ visitArray(value, { path, ancestors, eventId });
289
374
  } else {
290
- const prototype = Object.getPrototypeOf(value);
291
- if (prototype !== Object.prototype && prototype !== null) {
375
+ visitPlainObject(value, { path, ancestors, eventId });
376
+ }
377
+ ancestors.delete(value);
378
+ }
379
+ function visitArray(value, { path, ancestors, eventId }) {
380
+ for (let index = 0; index < value.length; index += 1) {
381
+ if (!(index in value)) {
292
382
  throw refuse(eventId, {
293
- path,
294
- reason: "is not a plain object (class instance, Date, Map or Set)"
295
- });
296
- }
297
- if (Object.getOwnPropertySymbols(value).length > 0) {
298
- throw refuse(eventId, { path, reason: "has a symbol key, which JSON drops" });
299
- }
300
- for (const key of Object.keys(value)) {
301
- visit(value[key], {
302
- path: `${path}.${key}`,
303
- ancestors,
304
- eventId
383
+ path: `${path}[${index}]`,
384
+ reason: "is an array hole, which JSON turns into null"
305
385
  });
306
386
  }
387
+ visit(value[index], { path: `${path}[${index}]`, ancestors, eventId });
388
+ }
389
+ }
390
+ function visitPlainObject(value, { path, ancestors, eventId }) {
391
+ const prototype = Object.getPrototypeOf(value);
392
+ if (prototype !== Object.prototype && prototype !== null) {
393
+ throw refuse(eventId, {
394
+ path,
395
+ reason: "is not a plain object (class instance, Date, Map or Set)"
396
+ });
397
+ }
398
+ if (Object.getOwnPropertySymbols(value).length > 0) {
399
+ throw refuse(eventId, { path, reason: "has a symbol key, which JSON drops" });
400
+ }
401
+ for (const key of Object.keys(value)) {
402
+ visit(value[key], {
403
+ path: `${path}.${key}`,
404
+ ancestors,
405
+ eventId
406
+ });
307
407
  }
308
- ancestors.delete(value);
309
408
  }
310
409
  function refuse(eventId, { path, reason }) {
311
410
  return new UnserializablePayloadError(
@@ -334,6 +433,22 @@ function encodeEnvelope(envelope) {
334
433
  [`${ATTRIBUTE_PREFIX}time`]: envelope.occurredAt.toISOString(),
335
434
  [`${ATTRIBUTE_PREFIX}datacontenttype`]: JSON_CONTENT_TYPE
336
435
  };
436
+ addOptionalAttributes(headers, envelope);
437
+ return {
438
+ content: Buffer.from(JSON.stringify(envelope.payload), "utf8"),
439
+ options: {
440
+ contentType: JSON_CONTENT_TYPE,
441
+ persistent: true,
442
+ messageId: envelope.id,
443
+ timestamp: Math.floor(envelope.occurredAt.getTime() / MILLISECONDS_PER_SECOND),
444
+ ...mirror("type", envelope.type),
445
+ ...mirror("appId", envelope.source),
446
+ ...envelope.correlationId === null ? {} : mirror("correlationId", envelope.correlationId),
447
+ headers
448
+ }
449
+ };
450
+ }
451
+ function addOptionalAttributes(headers, envelope) {
337
452
  if (envelope.key !== null) {
338
453
  headers[`${ATTRIBUTE_PREFIX}subject`] = envelope.key;
339
454
  }
@@ -350,19 +465,6 @@ function encodeEnvelope(envelope) {
350
465
  );
351
466
  headers[`${EVENT_HEADER_PREFIX}${name}`] = value;
352
467
  }
353
- return {
354
- content: Buffer.from(JSON.stringify(envelope.payload), "utf8"),
355
- options: {
356
- contentType: JSON_CONTENT_TYPE,
357
- persistent: true,
358
- messageId: envelope.id,
359
- timestamp: Math.floor(envelope.occurredAt.getTime() / MILLISECONDS_PER_SECOND),
360
- ...mirror("type", envelope.type),
361
- ...mirror("appId", envelope.source),
362
- ...envelope.correlationId === null ? {} : mirror("correlationId", envelope.correlationId),
363
- headers
364
- }
365
- };
366
468
  }
367
469
  function encodeStructuredEnvelope(envelope) {
368
470
  assertJsonPayload(envelope.payload, envelope.id);
@@ -547,18 +649,13 @@ function plainJsonDecoder(options) {
547
649
  if (properties.headers?.[SPEC_VERSION_HEADER] !== void 0 || mediaTypeOf(properties.contentType) === STRUCTURED_MEDIA_TYPE) {
548
650
  return decodeCloudEvent(message);
549
651
  }
550
- const plain = {
551
- routingKey: message.fields.routingKey,
552
- properties,
553
- headers: readForeignHeaders(properties),
554
- payload: parseJsonBody(message.content)
555
- };
556
- const type = resolveText(options.type, { message: plain, name: "type" });
652
+ const plain = plainMessageOf(message);
653
+ const type = RABBITMQ_EVENTS.text(options.type, { plain, name: "type" });
557
654
  if (type === void 0) {
558
655
  throw new MalformedMessageError("Message has no event type: the type option found none");
559
656
  }
560
657
  assertValidType(type, "The event type");
561
- const id = resolveText(options.id, { message: plain, name: "id" }) ?? nonEmptyText(properties.messageId);
658
+ const id = RABBITMQ_EVENTS.text(options.id, { plain, name: "id" }) ?? nonEmptyText(properties.messageId);
562
659
  if (id === void 0) {
563
660
  throw new MalformedMessageError(
564
661
  "Message has no id: set the messageId property on the producer or configure the id option"
@@ -567,8 +664,8 @@ function plainJsonDecoder(options) {
567
664
  return {
568
665
  id,
569
666
  type,
570
- source: resolveText(options.source, { message: plain, name: "source" }) ?? defaultSource(message),
571
- key: resolveText(options.key, { message: plain, name: "key" }) ?? null,
667
+ source: RABBITMQ_EVENTS.text(options.source, { plain, name: "source" }) ?? defaultSource(message),
668
+ key: RABBITMQ_EVENTS.text(options.key, { plain, name: "key" }) ?? null,
572
669
  payload: plain.payload,
573
670
  headers: plain.headers,
574
671
  correlationId: null,
@@ -577,27 +674,35 @@ function plainJsonDecoder(options) {
577
674
  };
578
675
  };
579
676
  }
580
- function completeDecodedEvent(decoded, message) {
581
- if (typeof decoded !== "object" || decoded === null) {
582
- throw new MalformedMessageError("The decoder must return an event object synchronously");
583
- }
584
- if ("then" in decoded) {
585
- observeRejection(decoded);
586
- throw new MalformedMessageError("The decoder must return an event object synchronously");
587
- }
588
- const event = decoded;
677
+ function plainMessageOf(message) {
678
+ const { properties } = message;
589
679
  return {
590
- id: event.id,
591
- type: event.type,
592
- source: event.source ?? defaultSource(message),
593
- key: event.key ?? null,
594
- payload: event.payload,
595
- headers: event.headers ?? {},
596
- correlationId: event.correlationId ?? null,
597
- causationId: event.causationId ?? null,
598
- occurredAt: event.occurredAt ?? propertyTimeOrNow(message.properties)
680
+ routingKey: message.fields.routingKey,
681
+ properties,
682
+ headers: readForeignHeaders(properties),
683
+ payload: parseJsonBody(message.content)
599
684
  };
600
685
  }
686
+ var RabbitMqDecodedEvents = class extends DecodedEventReader {
687
+ invalid(reason) {
688
+ return new MalformedMessageError(reason);
689
+ }
690
+ defaultSource(message) {
691
+ return defaultSource(message);
692
+ }
693
+ /** None: AMQP messages carry no ordering key. */
694
+ defaultKey() {
695
+ return null;
696
+ }
697
+ /** The `timestamp` property, else the time of receipt. */
698
+ defaultTime(message) {
699
+ return propertyTimeOrNow(message.properties);
700
+ }
701
+ };
702
+ var RABBITMQ_EVENTS = new RabbitMqDecodedEvents();
703
+ function completeDecodedEvent(decoded, message) {
704
+ return RABBITMQ_EVENTS.complete(decoded, message);
705
+ }
601
706
  function parseJsonBody(content) {
602
707
  let text2;
603
708
  try {
@@ -642,19 +747,6 @@ function readForeignHeaders(properties) {
642
747
  )
643
748
  );
644
749
  }
645
- function resolveText(option, { message, name }) {
646
- const value = typeof option === "function" ? option(message) : option;
647
- if (value === void 0 || value === null) {
648
- return void 0;
649
- }
650
- if (typeof value !== "string") {
651
- observeRejection(value);
652
- throw new MalformedMessageError(
653
- `The ${name} option returned ${typeof value}, expected a string`
654
- );
655
- }
656
- return value.length === 0 ? void 0 : value;
657
- }
658
750
  function resolveOccurredAt(option, message) {
659
751
  const value = option?.(message);
660
752
  if (value === void 0) {
@@ -666,11 +758,6 @@ function resolveOccurredAt(option, message) {
666
758
  }
667
759
  return value;
668
760
  }
669
- function observeRejection(value) {
670
- if (typeof value === "object" && value !== null && "then" in value) {
671
- Promise.resolve(value).catch(() => void 0);
672
- }
673
- }
674
761
 
675
762
  // src/codecs.ts
676
763
  var cloudEventsBinary = Object.freeze({
@@ -841,7 +928,7 @@ function resolveSettings(options) {
841
928
  };
842
929
  }
843
930
  function resolveBasics(options) {
844
- const exchange = nonEmpty(options.exchange ?? "relaybus", "exchange");
931
+ const exchange = NonEmptyText.of(options.exchange ?? "relaybus", "exchange");
845
932
  const failOnConnectError = options.failOnConnectError ?? true;
846
933
  if (!failOnConnectError && options.reconnect === false) {
847
934
  throw new ConfigurationError(
@@ -849,7 +936,7 @@ function resolveBasics(options) {
849
936
  );
850
937
  }
851
938
  const ordered = options.ordered ?? false;
852
- const prefetch = assertPositiveInteger(
939
+ const prefetch = PositiveInteger.of(
853
940
  options.prefetch ?? (ordered ? 1 : DEFAULT_PREFETCH),
854
941
  "prefetch"
855
942
  );
@@ -861,19 +948,19 @@ function resolveBasics(options) {
861
948
  }
862
949
  function resolveTopology(options, { exchange, prefetch }) {
863
950
  return {
864
- name: nonEmpty(options.name ?? "rabbitmq", "name"),
951
+ name: NonEmptyText.of(options.name ?? "rabbitmq", "name"),
865
952
  connect: resolveConnect(options),
866
953
  exchange,
867
954
  prefetch,
868
- queuePrefix: nonEmpty(options.queuePrefix ?? "relaybus", "queuePrefix"),
869
- consumerGroup: optionalNonEmpty(options.consumerGroup, "consumerGroup"),
870
- queue: optionalNonEmpty(options.queue, "queue"),
871
- deadLetterExchange: nonEmpty(
955
+ queuePrefix: NonEmptyText.of(options.queuePrefix ?? "relaybus", "queuePrefix"),
956
+ consumerGroup: NonEmptyText.optional(options.consumerGroup, "consumerGroup"),
957
+ queue: NonEmptyText.optional(options.queue, "queue"),
958
+ deadLetterExchange: NonEmptyText.of(
872
959
  options.deadLetterExchange ?? `${exchange}.dead`,
873
960
  "deadLetterExchange"
874
961
  ),
875
- deadLetterQueue: optionalNonEmpty(options.deadLetterQueue, "deadLetterQueue"),
876
- deliveryLimit: assertPositiveInteger(
962
+ deadLetterQueue: NonEmptyText.optional(options.deadLetterQueue, "deadLetterQueue"),
963
+ deliveryLimit: PositiveInteger.of(
877
964
  options.deliveryLimit ?? DEFAULT_DELIVERY_LIMIT,
878
965
  "deliveryLimit"
879
966
  )
@@ -884,7 +971,7 @@ function resolveRetries(options, { ordered, redelivery }) {
884
971
  handlerRetry: createRetryPolicy(options.handlerRetry, DEFAULT_HANDLER_RETRY),
885
972
  redelivery,
886
973
  retryLadder: ordered ? [] : buildRetryLadder(redelivery),
887
- redeliveryMaxAgeMs: positiveDuration(
974
+ redeliveryMaxAgeMs: Milliseconds.positive(
888
975
  options.redelivery?.maxAge ?? DEFAULT_REDELIVERY_MAX_AGE,
889
976
  "redelivery.maxAge"
890
977
  ),
@@ -896,8 +983,8 @@ function resolveRuntime(options, { failOnConnectError }) {
896
983
  reconnect: resolveReconnect(options.reconnect),
897
984
  failOnConnectError,
898
985
  allowUnroutable: options.allowUnroutable ?? false,
899
- publishTimeoutMs: positiveDuration(options.publishTimeout ?? "30s", "publishTimeout"),
900
- shutdownTimeoutMs: toMilliseconds(options.shutdownTimeout ?? "30s", "shutdownTimeout"),
986
+ publishTimeoutMs: Milliseconds.positive(options.publishTimeout ?? "30s", "publishTimeout"),
987
+ shutdownTimeoutMs: Milliseconds.of(options.shutdownTimeout ?? "30s", "shutdownTimeout"),
901
988
  ...resolveCodec(options),
902
989
  logger: options.logger ?? console,
903
990
  random: options.random ?? Math.random
@@ -950,22 +1037,6 @@ function resolveReconnect(reconnect) {
950
1037
  }
951
1038
  return createRetryPolicy({ ...reconnect, attempts: 1 }, DEFAULT_RECONNECT);
952
1039
  }
953
- function nonEmpty(value, name) {
954
- if (value.length === 0) {
955
- throw new ConfigurationError(`${name} must not be empty`);
956
- }
957
- return value;
958
- }
959
- function optionalNonEmpty(value, name) {
960
- return value === void 0 ? void 0 : nonEmpty(value, name);
961
- }
962
- function positiveDuration(value, name) {
963
- const milliseconds = toMilliseconds(value, name);
964
- if (milliseconds === 0) {
965
- throw new ConfigurationError(`${name} must be greater than zero`);
966
- }
967
- return milliseconds;
968
- }
969
1040
 
970
1041
  // src/topology.ts
971
1042
  function retryQueueName(queue, delayMs) {
@@ -1308,20 +1379,22 @@ var SerialQueue = class {
1308
1379
  };
1309
1380
 
1310
1381
  // src/rabbitmq-transport.ts
1311
- var NACK_MESSAGE = "message nacked";
1312
- var RabbitMqTransport = class {
1382
+ var RabbitMqTransport = class extends SubscribingTransport {
1313
1383
  name;
1314
1384
  system = "rabbitmq";
1315
1385
  #settings;
1316
1386
  #supervisor;
1387
+ #publishing;
1317
1388
  /** Serializes topology work so a subscription never races a reconnect. */
1318
1389
  #topologyWork = new SerialQueue();
1319
1390
  #live;
1320
1391
  #subscription;
1321
1392
  #closing;
1322
1393
  constructor(options) {
1394
+ super();
1323
1395
  this.#settings = resolveSettings(options);
1324
1396
  this.name = this.#settings.name;
1397
+ this.#publishing = new ConfirmedPublishing(this.name, this.#settings.publishTimeoutMs);
1325
1398
  this.#supervisor = new ConnectionSupervisor({
1326
1399
  connect: this.#settings.connect,
1327
1400
  reconnect: this.#settings.reconnect,
@@ -1363,7 +1436,7 @@ var RabbitMqTransport = class {
1363
1436
  async publish(envelope, destination) {
1364
1437
  assertShortString("Destination (routing key)", destination);
1365
1438
  const { content, options } = this.#settings.encode(envelope);
1366
- await this.#publishConfirmed({
1439
+ await this.#publishing.publish(this.#live?.publisher, {
1367
1440
  exchange: this.#settings.exchange,
1368
1441
  routingKey: destination,
1369
1442
  content,
@@ -1371,63 +1444,16 @@ var RabbitMqTransport = class {
1371
1444
  mandatory: !this.#settings.allowUnroutable
1372
1445
  });
1373
1446
  }
1374
- async #publishConfirmed(input) {
1375
- const { exchange, routingKey, content, options, mandatory } = input;
1376
- const publisher = this.#live?.publisher;
1377
- const eventId = options.messageId ?? "unknown";
1378
- if (publisher === void 0) {
1379
- throw new RabbitMqUnavailableError(
1380
- `[RabbitMqTransport](publish) Not connected to the broker | transport=${this.name} destination=${routingKey}`
1381
- );
1382
- }
1383
- const returnKey = unroutableKey(options.messageId, routingKey);
1384
- await new Promise((resolve, reject) => {
1385
- const cancelTimeout = schedule(systemClock, {
1386
- run: () => {
1387
- reject(
1388
- new RabbitMqUnavailableError(
1389
- `[RabbitMqTransport](publish) No broker confirm within ${this.#settings.publishTimeoutMs}ms | transport=${this.name} eventId=${eventId} destination=${routingKey}`
1390
- )
1391
- );
1392
- },
1393
- delayMs: this.#settings.publishTimeoutMs
1394
- });
1395
- const onConfirm = (error) => {
1396
- cancelTimeout();
1397
- const returned = takeUnroutable(publisher.unroutable, returnKey);
1398
- if (error) {
1399
- reject(this.#toPublishError(error, { eventId, destination: routingKey }));
1400
- } else if (returned) {
1401
- reject(
1402
- new UnroutableMessageError(
1403
- `[RabbitMqTransport](publish) No queue is bound to the routing key | transport=${this.name} eventId=${eventId} destination=${routingKey}`
1404
- )
1405
- );
1406
- } else {
1407
- resolve();
1408
- }
1409
- };
1410
- try {
1411
- publisher.channel.publish(
1412
- exchange,
1413
- routingKey,
1414
- content,
1415
- { ...options, mandatory },
1416
- onConfirm
1417
- );
1418
- } catch (error) {
1419
- cancelTimeout();
1420
- reject(this.#toPublishError(error, { eventId, destination: routingKey }));
1421
- }
1422
- });
1423
- }
1424
1447
  /**
1425
1448
  * Binds one durable quorum queue to `destinations` and delivers its messages
1426
1449
  * to `onMessage`. Call it once per transport; give each subscription its own
1427
1450
  * transport instance, with its own `name` and `consumerGroup`.
1428
1451
  */
1429
- // eslint-disable-next-line relaybus/max-two-params -- the Transport.subscribe signature
1430
- async subscribe(destinations, onMessage, hooks) {
1452
+ async openSubscription({
1453
+ destinations,
1454
+ onMessage,
1455
+ hooks
1456
+ }) {
1431
1457
  if (this.#subscription !== void 0) {
1432
1458
  throw new ConfigurationError(
1433
1459
  `Transport '${this.name}' already has a subscription; create another transport for another consumer group`
@@ -1438,34 +1464,8 @@ var RabbitMqTransport = class {
1438
1464
  }
1439
1465
  const queue = resolveQueueName(this.#settings);
1440
1466
  const subscription = {
1441
- topology: {
1442
- exchange: this.#settings.exchange,
1443
- queue,
1444
- deadLetterExchange: this.#settings.deadLetterExchange,
1445
- deadLetterQueue: this.#settings.deadLetterQueue ?? `${queue}.dead`,
1446
- deliveryLimit: this.#settings.deliveryLimit,
1447
- ordered: this.#settings.ordered,
1448
- retryDelaysMs: this.#settings.retryLadder,
1449
- destinations
1450
- },
1451
- consumer: new QueueConsumer({
1452
- queue,
1453
- prefetch: this.#settings.prefetch,
1454
- handlerRetry: this.#settings.handlerRetry,
1455
- redelivery: this.#settings.redelivery,
1456
- retryLadder: this.#settings.retryLadder,
1457
- redeliveryMaxAgeMs: this.#settings.redeliveryMaxAgeMs,
1458
- ordered: this.#settings.ordered,
1459
- deadLetterExchange: this.#settings.deadLetterExchange,
1460
- republish: ({ exchange, routingKey }, { content, options }) => this.#publishConfirmed({ exchange, routingKey, content, options, mandatory: true }),
1461
- shutdownTimeoutMs: this.#settings.shutdownTimeoutMs,
1462
- onMessage,
1463
- ...hooks === void 0 ? {} : { onInvalidMessage: hooks.onInvalidMessage },
1464
- decode: this.#settings.decode,
1465
- onConsumerLost: (reason) => this.#supervisor.recover(reason),
1466
- logger: this.#settings.logger,
1467
- random: this.#settings.random
1468
- })
1467
+ topology: this.#topologyOf(queue, destinations),
1468
+ consumer: this.#createConsumer(queue, { onMessage, hooks })
1469
1469
  };
1470
1470
  await this.#topologyWork.run(async () => {
1471
1471
  this.#subscription = subscription;
@@ -1474,6 +1474,44 @@ var RabbitMqTransport = class {
1474
1474
  }
1475
1475
  });
1476
1476
  }
1477
+ #topologyOf(queue, destinations) {
1478
+ return {
1479
+ exchange: this.#settings.exchange,
1480
+ queue,
1481
+ deadLetterExchange: this.#settings.deadLetterExchange,
1482
+ deadLetterQueue: this.#settings.deadLetterQueue ?? `${queue}.dead`,
1483
+ deliveryLimit: this.#settings.deliveryLimit,
1484
+ ordered: this.#settings.ordered,
1485
+ retryDelaysMs: this.#settings.retryLadder,
1486
+ destinations
1487
+ };
1488
+ }
1489
+ #createConsumer(queue, { onMessage, hooks }) {
1490
+ return new QueueConsumer({
1491
+ queue,
1492
+ prefetch: this.#settings.prefetch,
1493
+ handlerRetry: this.#settings.handlerRetry,
1494
+ redelivery: this.#settings.redelivery,
1495
+ retryLadder: this.#settings.retryLadder,
1496
+ redeliveryMaxAgeMs: this.#settings.redeliveryMaxAgeMs,
1497
+ ordered: this.#settings.ordered,
1498
+ deadLetterExchange: this.#settings.deadLetterExchange,
1499
+ republish: ({ exchange, routingKey }, { content, options }) => this.#publishing.publish(this.#live?.publisher, {
1500
+ exchange,
1501
+ routingKey,
1502
+ content,
1503
+ options,
1504
+ mandatory: true
1505
+ }),
1506
+ shutdownTimeoutMs: this.#settings.shutdownTimeoutMs,
1507
+ onMessage,
1508
+ ...hooks === void 0 ? {} : { onInvalidMessage: hooks.onInvalidMessage },
1509
+ decode: this.#settings.decode,
1510
+ onConsumerLost: (reason) => this.#supervisor.recover(reason),
1511
+ logger: this.#settings.logger,
1512
+ random: this.#settings.random
1513
+ });
1514
+ }
1477
1515
  /**
1478
1516
  * Stops consuming, waits up to `shutdownTimeout` for running handlers
1479
1517
  * (messages still being retried go back to the queue), then closes the
@@ -1543,36 +1581,7 @@ var RabbitMqTransport = class {
1543
1581
  this.#live = void 0;
1544
1582
  this.#subscription?.consumer.detach();
1545
1583
  }
1546
- #toPublishError(error, { eventId, destination }) {
1547
- const { message } = toErrorInfo(error);
1548
- const detail = `transport=${this.name} eventId=${eventId} destination=${destination} error=${message}`;
1549
- return message === NACK_MESSAGE ? new RabbitMqPublishRejectedError(
1550
- `[RabbitMqTransport](publish) Broker nacked the message | ${detail}`,
1551
- { cause: error }
1552
- ) : new RabbitMqUnavailableError(`[RabbitMqTransport](publish) Publish failed | ${detail}`, {
1553
- cause: error
1554
- });
1555
- }
1556
1584
  };
1557
- function unroutableKey(messageId, routingKey) {
1558
- return `${String(messageId)}
1559
- ${routingKey}`;
1560
- }
1561
- function recordUnroutable(unroutable, key) {
1562
- unroutable.set(key, (unroutable.get(key) ?? 0) + 1);
1563
- }
1564
- function takeUnroutable(unroutable, key) {
1565
- const count = unroutable.get(key);
1566
- if (count === void 0) {
1567
- return false;
1568
- }
1569
- if (count === 1) {
1570
- unroutable.delete(key);
1571
- } else {
1572
- unroutable.set(key, count - 1);
1573
- }
1574
- return true;
1575
- }
1576
1585
 
1577
1586
  export { AmqpLimitExceededError, MalformedMessageError, RabbitMqPublishRejectedError, RabbitMqTransport, RabbitMqUnavailableError, RabbitMqUnencodableEventError, UnroutableMessageError, UnserializablePayloadError, cloudEventsBinary, cloudEventsStructured, decodeCloudEvent, jsonEnvelope, plainJsonDecoder };
1578
1587
  //# sourceMappingURL=index.js.map