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