runmq 1.5.1 → 2.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.
- package/README.md +105 -106
- package/dist/index.cjs +337 -142
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -3
- package/dist/index.d.ts +46 -3
- package/dist/index.js +337 -142
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -157,9 +157,9 @@ var RabbitMQClientChannel = class {
|
|
|
157
157
|
arguments: args
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
|
-
publish(exchange, routingKey, content, options) {
|
|
160
|
+
async publish(exchange, routingKey, content, options) {
|
|
161
161
|
var _a2;
|
|
162
|
-
this.channel.basicPublish({
|
|
162
|
+
await this.channel.basicPublish({
|
|
163
163
|
exchange,
|
|
164
164
|
routingKey,
|
|
165
165
|
correlationId: options == null ? void 0 : options.correlationId,
|
|
@@ -176,7 +176,9 @@ var RabbitMQClientChannel = class {
|
|
|
176
176
|
userId: options == null ? void 0 : options.userId,
|
|
177
177
|
appId: options == null ? void 0 : options.appId
|
|
178
178
|
}, content);
|
|
179
|
-
|
|
179
|
+
}
|
|
180
|
+
async confirmSelect() {
|
|
181
|
+
await this.channel.confirmSelect();
|
|
180
182
|
}
|
|
181
183
|
async consume(queue, onMessage, options) {
|
|
182
184
|
const result = await this.channel.basicConsume({
|
|
@@ -279,12 +281,14 @@ var RabbitMQClientAdapter = class {
|
|
|
279
281
|
this.logger = logger;
|
|
280
282
|
this.isConnected = false;
|
|
281
283
|
this.acquiredChannels = [];
|
|
284
|
+
this.isShuttingDown = false;
|
|
282
285
|
}
|
|
283
286
|
async connect() {
|
|
284
287
|
try {
|
|
285
288
|
if (this.connection && this.isConnected) {
|
|
286
289
|
return this.connection;
|
|
287
290
|
}
|
|
291
|
+
this.isShuttingDown = false;
|
|
288
292
|
if (this.connection) {
|
|
289
293
|
try {
|
|
290
294
|
await this.connection.close();
|
|
@@ -333,21 +337,52 @@ var RabbitMQClientAdapter = class {
|
|
|
333
337
|
);
|
|
334
338
|
}
|
|
335
339
|
}
|
|
336
|
-
async getChannel() {
|
|
340
|
+
async getChannel(callbacks) {
|
|
337
341
|
const connection = await this.connect();
|
|
338
342
|
const rawChannel = await connection.acquire();
|
|
343
|
+
const channelId = rawChannel.id;
|
|
344
|
+
rawChannel.on("error", (err) => {
|
|
345
|
+
var _a2;
|
|
346
|
+
this.logger.error("RabbitMQ channel error:", { channelId, error: err });
|
|
347
|
+
try {
|
|
348
|
+
(_a2 = callbacks == null ? void 0 : callbacks.onError) == null ? void 0 : _a2.call(callbacks, err);
|
|
349
|
+
} catch (cbErr) {
|
|
350
|
+
this.logger.error("RabbitMQ channel onError callback threw:", { channelId, error: cbErr });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
rawChannel.on("close", () => {
|
|
354
|
+
var _a2;
|
|
355
|
+
this.logger.warn("RabbitMQ channel closed", { channelId });
|
|
356
|
+
const idx = this.acquiredChannels.indexOf(rawChannel);
|
|
357
|
+
if (idx >= 0) {
|
|
358
|
+
this.acquiredChannels.splice(idx, 1);
|
|
359
|
+
}
|
|
360
|
+
if (this.isShuttingDown) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
(_a2 = callbacks == null ? void 0 : callbacks.onClose) == null ? void 0 : _a2.call(callbacks);
|
|
365
|
+
} catch (cbErr) {
|
|
366
|
+
this.logger.error("RabbitMQ channel onClose callback threw:", { channelId, error: cbErr });
|
|
367
|
+
}
|
|
368
|
+
});
|
|
339
369
|
this.acquiredChannels.push(rawChannel);
|
|
340
370
|
return new RabbitMQClientChannel(rawChannel);
|
|
341
371
|
}
|
|
342
372
|
async getDefaultChannel() {
|
|
343
373
|
if (!this.defaultChannel) {
|
|
344
|
-
this.defaultChannel = await this.getChannel(
|
|
374
|
+
this.defaultChannel = await this.getChannel({
|
|
375
|
+
onClose: () => {
|
|
376
|
+
this.defaultChannel = void 0;
|
|
377
|
+
}
|
|
378
|
+
});
|
|
345
379
|
}
|
|
346
380
|
return this.defaultChannel;
|
|
347
381
|
}
|
|
348
382
|
async disconnect() {
|
|
349
383
|
const conn = this.connection;
|
|
350
384
|
const channels = this.acquiredChannels;
|
|
385
|
+
this.isShuttingDown = true;
|
|
351
386
|
this.connection = void 0;
|
|
352
387
|
this.defaultChannel = void 0;
|
|
353
388
|
this.isConnected = false;
|
|
@@ -420,20 +455,33 @@ var RabbitMQMessage = class _RabbitMQMessage {
|
|
|
420
455
|
this.headers = headers;
|
|
421
456
|
}
|
|
422
457
|
/**
|
|
423
|
-
* Acknowledges the message.
|
|
458
|
+
* Acknowledges the message. Returns true on success, false if the
|
|
459
|
+
* underlying channel rejected the call (e.g. closed mid-flight).
|
|
460
|
+
* Plumbing errors are intentionally swallowed: the broker will redeliver
|
|
461
|
+
* unacked messages on channel close, so escalating here only crashes
|
|
462
|
+
* the consumer for no recovery benefit.
|
|
424
463
|
*/
|
|
425
464
|
ack() {
|
|
426
|
-
if (this.amqpMessage)
|
|
465
|
+
if (!this.amqpMessage) return false;
|
|
466
|
+
try {
|
|
427
467
|
this.channel.ack(this.amqpMessage);
|
|
468
|
+
return true;
|
|
469
|
+
} catch (e) {
|
|
470
|
+
return false;
|
|
428
471
|
}
|
|
429
472
|
}
|
|
430
473
|
/**
|
|
431
|
-
* Negatively acknowledges the message.
|
|
474
|
+
* Negatively acknowledges the message. Returns true on success, false if
|
|
475
|
+
* the underlying channel rejected the call. See `ack()` for rationale.
|
|
432
476
|
* @param requeue - Whether to requeue the message (default: false)
|
|
433
477
|
*/
|
|
434
478
|
nack(requeue = false) {
|
|
435
|
-
if (this.amqpMessage)
|
|
479
|
+
if (!this.amqpMessage) return false;
|
|
480
|
+
try {
|
|
436
481
|
this.channel.nack(this.amqpMessage, false, requeue);
|
|
482
|
+
return true;
|
|
483
|
+
} catch (e) {
|
|
484
|
+
return false;
|
|
437
485
|
}
|
|
438
486
|
}
|
|
439
487
|
static from(messageData, channel, props, amqpMessage = null) {
|
|
@@ -450,13 +498,20 @@ var RabbitMQMessage = class _RabbitMQMessage {
|
|
|
450
498
|
|
|
451
499
|
// src/core/consumer/processors/RunMQSucceededMessageAcknowledgerProcessor.ts
|
|
452
500
|
var RunMQSucceededMessageAcknowledgerProcessor = class {
|
|
453
|
-
constructor(consumer) {
|
|
501
|
+
constructor(consumer, logger) {
|
|
454
502
|
this.consumer = consumer;
|
|
503
|
+
this.logger = logger;
|
|
455
504
|
}
|
|
456
505
|
async consume(message) {
|
|
506
|
+
var _a2;
|
|
457
507
|
const result = await this.consumer.consume(message);
|
|
458
508
|
if (result) {
|
|
459
|
-
message.ack();
|
|
509
|
+
const acked = message.ack();
|
|
510
|
+
if (!acked) {
|
|
511
|
+
(_a2 = this.logger) == null ? void 0 : _a2.warn("Failed to ack message \u2014 channel likely closed. Broker will redeliver.", {
|
|
512
|
+
correlationId: message.correlationId
|
|
513
|
+
});
|
|
514
|
+
}
|
|
460
515
|
}
|
|
461
516
|
return result;
|
|
462
517
|
}
|
|
@@ -464,40 +519,26 @@ var RunMQSucceededMessageAcknowledgerProcessor = class {
|
|
|
464
519
|
|
|
465
520
|
// src/core/consumer/processors/RunMQFailedMessageRejecterProcessor.ts
|
|
466
521
|
var RunMQFailedMessageRejecterProcessor = class {
|
|
467
|
-
constructor(consumer) {
|
|
522
|
+
constructor(consumer, logger) {
|
|
468
523
|
this.consumer = consumer;
|
|
524
|
+
this.logger = logger;
|
|
469
525
|
}
|
|
470
526
|
async consume(message) {
|
|
527
|
+
var _a2;
|
|
471
528
|
try {
|
|
472
529
|
return await this.consumer.consume(message);
|
|
473
530
|
} catch (e) {
|
|
474
|
-
message.nack(false);
|
|
531
|
+
const nacked = message.nack(false);
|
|
532
|
+
if (!nacked) {
|
|
533
|
+
(_a2 = this.logger) == null ? void 0 : _a2.warn("Failed to nack message \u2014 channel likely closed. Broker will redeliver.", {
|
|
534
|
+
correlationId: message.correlationId
|
|
535
|
+
});
|
|
536
|
+
}
|
|
475
537
|
return false;
|
|
476
538
|
}
|
|
477
539
|
}
|
|
478
540
|
};
|
|
479
541
|
|
|
480
|
-
// src/core/message/RunMQMessage.ts
|
|
481
|
-
var RunMQMessage = class {
|
|
482
|
-
static isValid(obj) {
|
|
483
|
-
if (typeof obj === "object" && obj !== null) {
|
|
484
|
-
return "message" in obj && "meta" in obj && typeof obj.message === "object" && obj.message !== null && Array.isArray(obj.message) === false && typeof obj.meta === "object" && obj.meta !== null && "id" in obj.meta && "correlationId" in obj.meta && "publishedAt" in obj.meta && typeof obj.meta.id === "string" && typeof obj.meta.correlationId === "string" && typeof obj.meta.publishedAt === "number";
|
|
485
|
-
}
|
|
486
|
-
return false;
|
|
487
|
-
}
|
|
488
|
-
constructor(message, meta) {
|
|
489
|
-
this.message = message;
|
|
490
|
-
this.meta = meta;
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
var RunMQMessageMeta = class {
|
|
494
|
-
constructor(id, publishedAt, correlationId) {
|
|
495
|
-
this.id = id;
|
|
496
|
-
this.correlationId = correlationId;
|
|
497
|
-
this.publishedAt = publishedAt;
|
|
498
|
-
}
|
|
499
|
-
};
|
|
500
|
-
|
|
501
542
|
// src/core/consumer/ConsumerCreatorUtils.ts
|
|
502
543
|
var ConsumerCreatorUtils = class {
|
|
503
544
|
static getDLQTopicName(topic) {
|
|
@@ -514,11 +555,11 @@ var ConsumerCreatorUtils = class {
|
|
|
514
555
|
// src/core/consumer/processors/RunMQRetriesCheckerProcessor.ts
|
|
515
556
|
var _a;
|
|
516
557
|
var RunMQRetriesCheckerProcessor = class {
|
|
517
|
-
constructor(consumer, config,
|
|
558
|
+
constructor(consumer, config, logger, logFullMessagePayload = false) {
|
|
518
559
|
this.consumer = consumer;
|
|
519
560
|
this.config = config;
|
|
520
|
-
this.DLQPublisher = DLQPublisher;
|
|
521
561
|
this.logger = logger;
|
|
562
|
+
this.logFullMessagePayload = logFullMessagePayload;
|
|
522
563
|
this.maxAttempts = (_a = this.config.attempts) != null ? _a : DEFAULTS.PROCESSING_ATTEMPTS;
|
|
523
564
|
}
|
|
524
565
|
async consume(message) {
|
|
@@ -527,7 +568,16 @@ var RunMQRetriesCheckerProcessor = class {
|
|
|
527
568
|
} catch (e) {
|
|
528
569
|
if (this.hasReachedMaxRetries(message)) {
|
|
529
570
|
this.logMaxRetriesReached(message);
|
|
530
|
-
|
|
571
|
+
try {
|
|
572
|
+
await this.moveToFinalDeadLetter(message);
|
|
573
|
+
} catch (publishError) {
|
|
574
|
+
this.logger.error("Failed to publish to DLQ \u2014 message will be redelivered", {
|
|
575
|
+
correlationId: message.correlationId,
|
|
576
|
+
cause: publishError instanceof Error ? publishError.message : String(publishError)
|
|
577
|
+
});
|
|
578
|
+
message.nack(false);
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
531
581
|
this.acknowledgeMessage(message);
|
|
532
582
|
return false;
|
|
533
583
|
}
|
|
@@ -541,44 +591,38 @@ var RunMQRetriesCheckerProcessor = class {
|
|
|
541
591
|
logMaxRetriesReached(message) {
|
|
542
592
|
this.logger.error(
|
|
543
593
|
`Message reached maximum attempts. Moving to dead-letter queue.`,
|
|
544
|
-
{
|
|
545
|
-
|
|
594
|
+
__spreadValues({
|
|
595
|
+
correlationId: message.correlationId,
|
|
596
|
+
messageId: message.id,
|
|
546
597
|
attempts: this.getRejectionCount(message),
|
|
547
598
|
max: this.maxAttempts
|
|
548
|
-
}
|
|
599
|
+
}, this.logFullMessagePayload ? { message: message.message } : {})
|
|
549
600
|
);
|
|
550
601
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
const parsed = JSON.parse(message.message);
|
|
567
|
-
if (RunMQMessage.isValid(parsed)) {
|
|
568
|
-
return parsed.message;
|
|
569
|
-
}
|
|
570
|
-
} catch (e) {
|
|
602
|
+
// Republish the original AMQP body verbatim so the envelope (including
|
|
603
|
+
// publishedAt) is preserved end-to-end for audit/replay. The consumer
|
|
604
|
+
// channel runs in confirm mode (see RunMQConsumerCreator), so awaiting
|
|
605
|
+
// this publish surfaces broker-side rejections instead of dropping them.
|
|
606
|
+
async moveToFinalDeadLetter(message) {
|
|
607
|
+
if (!message.amqpMessage) return;
|
|
608
|
+
await message.channel.publish(
|
|
609
|
+
Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME,
|
|
610
|
+
ConsumerCreatorUtils.getDLQTopicName(this.config.name),
|
|
611
|
+
message.amqpMessage.content,
|
|
612
|
+
{
|
|
613
|
+
correlationId: message.correlationId,
|
|
614
|
+
messageId: message.id,
|
|
615
|
+
headers: message.headers,
|
|
616
|
+
persistent: true
|
|
571
617
|
}
|
|
572
|
-
|
|
573
|
-
return message.message;
|
|
618
|
+
);
|
|
574
619
|
}
|
|
575
620
|
acknowledgeMessage(message) {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
throw error;
|
|
621
|
+
const acked = message.ack();
|
|
622
|
+
if (!acked) {
|
|
623
|
+
this.logger.warn("Failed to ack message after publishing to final dead letter \u2014 channel likely closed. Broker will redeliver.", {
|
|
624
|
+
correlationId: message.correlationId
|
|
625
|
+
});
|
|
582
626
|
}
|
|
583
627
|
}
|
|
584
628
|
getRejectionCount(message) {
|
|
@@ -592,9 +636,10 @@ var RunMQRetriesCheckerProcessor = class {
|
|
|
592
636
|
|
|
593
637
|
// src/core/consumer/processors/RunMQFailureLoggerProcessor.ts
|
|
594
638
|
var RunMQFailureLoggerProcessor = class {
|
|
595
|
-
constructor(consumer, logger) {
|
|
639
|
+
constructor(consumer, logger, logFullMessagePayload = false) {
|
|
596
640
|
this.consumer = consumer;
|
|
597
641
|
this.logger = logger;
|
|
642
|
+
this.logFullMessagePayload = logFullMessagePayload;
|
|
598
643
|
}
|
|
599
644
|
async consume(message) {
|
|
600
645
|
try {
|
|
@@ -602,9 +647,10 @@ var RunMQFailureLoggerProcessor = class {
|
|
|
602
647
|
} catch (e) {
|
|
603
648
|
this.logger.error(
|
|
604
649
|
"Message processing failed",
|
|
605
|
-
{
|
|
606
|
-
|
|
607
|
-
|
|
650
|
+
__spreadValues({
|
|
651
|
+
correlationId: message.correlationId,
|
|
652
|
+
messageId: message.id
|
|
653
|
+
}, this.logFullMessagePayload ? { message: message.message } : {}),
|
|
608
654
|
e instanceof Error ? e.stack : void 0
|
|
609
655
|
);
|
|
610
656
|
throw e;
|
|
@@ -612,39 +658,24 @@ var RunMQFailureLoggerProcessor = class {
|
|
|
612
658
|
}
|
|
613
659
|
};
|
|
614
660
|
|
|
615
|
-
// src/core/
|
|
616
|
-
var
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
661
|
+
// src/core/message/RunMQMessage.ts
|
|
662
|
+
var RunMQMessage = class {
|
|
663
|
+
static isValid(obj) {
|
|
664
|
+
if (typeof obj === "object" && obj !== null) {
|
|
665
|
+
return "message" in obj && "meta" in obj && typeof obj.message === "object" && obj.message !== null && Array.isArray(obj.message) === false && typeof obj.meta === "object" && obj.meta !== null && "id" in obj.meta && "correlationId" in obj.meta && "publishedAt" in obj.meta && typeof obj.meta.id === "string" && typeof obj.meta.correlationId === "string" && typeof obj.meta.publishedAt === "number";
|
|
666
|
+
}
|
|
667
|
+
return false;
|
|
621
668
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
return true;
|
|
669
|
+
constructor(message, meta) {
|
|
670
|
+
this.message = message;
|
|
671
|
+
this.meta = meta;
|
|
626
672
|
}
|
|
627
673
|
};
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
this.
|
|
633
|
-
this.logger = logger;
|
|
634
|
-
}
|
|
635
|
-
async consume(message) {
|
|
636
|
-
try {
|
|
637
|
-
return await this.consumer.consume(message);
|
|
638
|
-
} catch (e) {
|
|
639
|
-
if (e instanceof Error) {
|
|
640
|
-
this.logger.error(e.message, e.stack);
|
|
641
|
-
throw e;
|
|
642
|
-
} else {
|
|
643
|
-
const errorString = JSON.stringify(e);
|
|
644
|
-
this.logger.error(errorString);
|
|
645
|
-
throw new Error(errorString);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
674
|
+
var RunMQMessageMeta = class {
|
|
675
|
+
constructor(id, publishedAt, correlationId) {
|
|
676
|
+
this.id = id;
|
|
677
|
+
this.correlationId = correlationId;
|
|
678
|
+
this.publishedAt = publishedAt;
|
|
648
679
|
}
|
|
649
680
|
};
|
|
650
681
|
|
|
@@ -653,6 +684,17 @@ var import_ajv = __toESM(require("ajv"), 1);
|
|
|
653
684
|
var AjvSchemaValidator = class {
|
|
654
685
|
constructor() {
|
|
655
686
|
this.lastValidator = null;
|
|
687
|
+
/**
|
|
688
|
+
* Cache of compiled validators, keyed by schema identity.
|
|
689
|
+
*
|
|
690
|
+
* `ajv.compile()` codegens an optimized JS function from the schema
|
|
691
|
+
* (typically 2-10ms for non-trivial schemas). Without this cache the
|
|
692
|
+
* compile would run on every message — at high throughput it dominates
|
|
693
|
+
* CPU usage.
|
|
694
|
+
*
|
|
695
|
+
* WeakMap so we don't pin schemas in memory if a processor is removed.
|
|
696
|
+
*/
|
|
697
|
+
this.compiled = /* @__PURE__ */ new WeakMap();
|
|
656
698
|
this.ajv = new import_ajv.default({
|
|
657
699
|
allErrors: true,
|
|
658
700
|
verbose: true,
|
|
@@ -660,8 +702,14 @@ var AjvSchemaValidator = class {
|
|
|
660
702
|
});
|
|
661
703
|
}
|
|
662
704
|
validate(schema, data) {
|
|
663
|
-
|
|
664
|
-
|
|
705
|
+
const key = schema;
|
|
706
|
+
let validator = this.compiled.get(key);
|
|
707
|
+
if (!validator) {
|
|
708
|
+
validator = this.ajv.compile(schema);
|
|
709
|
+
this.compiled.set(key, validator);
|
|
710
|
+
}
|
|
711
|
+
this.lastValidator = validator;
|
|
712
|
+
return validator(data);
|
|
665
713
|
}
|
|
666
714
|
getError() {
|
|
667
715
|
if (!this.lastValidator || !this.lastValidator.errors) {
|
|
@@ -747,18 +795,111 @@ var DefaultDeserializer = class {
|
|
|
747
795
|
}
|
|
748
796
|
};
|
|
749
797
|
|
|
798
|
+
// src/core/consumer/processors/RunMQSchemaFailureProcessor.ts
|
|
799
|
+
var RunMQSchemaFailureProcessor = class {
|
|
800
|
+
constructor(consumer, config, DLQPublisher, logger) {
|
|
801
|
+
this.consumer = consumer;
|
|
802
|
+
this.config = config;
|
|
803
|
+
this.DLQPublisher = DLQPublisher;
|
|
804
|
+
this.logger = logger;
|
|
805
|
+
}
|
|
806
|
+
async consume(message) {
|
|
807
|
+
try {
|
|
808
|
+
return await this.consumer.consume(message);
|
|
809
|
+
} catch (e) {
|
|
810
|
+
if (this.shouldRouteToDLQ(e)) {
|
|
811
|
+
this.logger.warn("Schema validation failed \u2014 routing message to DLQ.", {
|
|
812
|
+
correlationId: message.correlationId,
|
|
813
|
+
error: e instanceof RunMQSchemaValidationError ? e.error : void 0
|
|
814
|
+
});
|
|
815
|
+
this.routeToDLQ(message);
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
throw e;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
shouldRouteToDLQ(e) {
|
|
822
|
+
var _a2;
|
|
823
|
+
if (!(e instanceof RunMQSchemaValidationError)) return false;
|
|
824
|
+
return ((_a2 = this.config.messageSchema) == null ? void 0 : _a2.failureStrategy) === "dlq";
|
|
825
|
+
}
|
|
826
|
+
routeToDLQ(message) {
|
|
827
|
+
const dlqMessage = new RabbitMQMessage(
|
|
828
|
+
this.extractOriginalPayload(message),
|
|
829
|
+
message.id,
|
|
830
|
+
message.correlationId,
|
|
831
|
+
message.channel,
|
|
832
|
+
message.amqpMessage,
|
|
833
|
+
message.headers
|
|
834
|
+
);
|
|
835
|
+
this.DLQPublisher.publish(
|
|
836
|
+
ConsumerCreatorUtils.getDLQTopicName(this.config.name),
|
|
837
|
+
dlqMessage
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
extractOriginalPayload(message) {
|
|
841
|
+
if (typeof message.message === "string") {
|
|
842
|
+
try {
|
|
843
|
+
const parsed = JSON.parse(message.message);
|
|
844
|
+
if (RunMQMessage.isValid(parsed)) {
|
|
845
|
+
return parsed.message;
|
|
846
|
+
}
|
|
847
|
+
} catch (e) {
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return message.message;
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
// src/core/consumer/processors/RunMQBaseProcessor.ts
|
|
855
|
+
var RunMQBaseProcessor = class {
|
|
856
|
+
constructor(handler, processorConfig, serializer) {
|
|
857
|
+
this.handler = handler;
|
|
858
|
+
this.processorConfig = processorConfig;
|
|
859
|
+
this.serializer = serializer;
|
|
860
|
+
}
|
|
861
|
+
async consume(message) {
|
|
862
|
+
const rabbitMQMessage = this.serializer.deserialize(message.message, this.processorConfig);
|
|
863
|
+
await this.handler(rabbitMQMessage);
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
|
|
868
|
+
// src/core/consumer/processors/RunMQExceptionLoggerProcessor.ts
|
|
869
|
+
var RunMQExceptionLoggerProcessor = class {
|
|
870
|
+
constructor(consumer, logger) {
|
|
871
|
+
this.consumer = consumer;
|
|
872
|
+
this.logger = logger;
|
|
873
|
+
}
|
|
874
|
+
async consume(message) {
|
|
875
|
+
try {
|
|
876
|
+
return await this.consumer.consume(message);
|
|
877
|
+
} catch (e) {
|
|
878
|
+
if (e instanceof Error) {
|
|
879
|
+
this.logger.error(e.message, e.stack);
|
|
880
|
+
throw e;
|
|
881
|
+
} else {
|
|
882
|
+
const errorString = JSON.stringify(e);
|
|
883
|
+
this.logger.error(errorString);
|
|
884
|
+
throw new Error(errorString);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
|
|
750
890
|
// src/core/publisher/producers/RunMQFailureLoggerProducer.ts
|
|
751
891
|
var RunMQFailureLoggerProducer = class {
|
|
752
892
|
constructor(producer, logger) {
|
|
753
893
|
this.producer = producer;
|
|
754
894
|
this.logger = logger;
|
|
755
895
|
}
|
|
756
|
-
publish(topic, message) {
|
|
896
|
+
async publish(topic, message) {
|
|
757
897
|
try {
|
|
758
|
-
this.producer.publish(topic, message);
|
|
898
|
+
await this.producer.publish(topic, message);
|
|
759
899
|
} catch (e) {
|
|
760
900
|
this.logger.error("Message publishing failed", {
|
|
761
|
-
|
|
901
|
+
topic,
|
|
902
|
+
correlationId: message.correlationId,
|
|
762
903
|
error: e instanceof Error ? e.message : JSON.stringify(e),
|
|
763
904
|
stack: e instanceof Error ? e.stack : void 0
|
|
764
905
|
});
|
|
@@ -773,7 +914,7 @@ var RunMQBaseProducer = class {
|
|
|
773
914
|
this.serializer = serializer;
|
|
774
915
|
this.exchange = exchange;
|
|
775
916
|
}
|
|
776
|
-
publish(topic, message) {
|
|
917
|
+
async publish(topic, message) {
|
|
777
918
|
const runMQMessage = new RunMQMessage(
|
|
778
919
|
message.message,
|
|
779
920
|
new RunMQMessageMeta(
|
|
@@ -783,7 +924,7 @@ var RunMQBaseProducer = class {
|
|
|
783
924
|
)
|
|
784
925
|
);
|
|
785
926
|
const serialized = this.serializer.serialize(runMQMessage);
|
|
786
|
-
message.channel.publish(this.exchange, topic, Buffer.from(serialized), {
|
|
927
|
+
await message.channel.publish(this.exchange, topic, Buffer.from(serialized), {
|
|
787
928
|
correlationId: message.correlationId,
|
|
788
929
|
messageId: message.id,
|
|
789
930
|
headers: message.headers,
|
|
@@ -1208,9 +1349,10 @@ var RunMQMetadataManager = class {
|
|
|
1208
1349
|
|
|
1209
1350
|
// src/core/consumer/RunMQConsumerCreator.ts
|
|
1210
1351
|
var RunMQConsumerCreator = class {
|
|
1211
|
-
constructor(client, logger, managementConfig) {
|
|
1352
|
+
constructor(client, logger, managementConfig, logFullMessagePayload = false) {
|
|
1212
1353
|
this.client = client;
|
|
1213
1354
|
this.logger = logger;
|
|
1355
|
+
this.logFullMessagePayload = logFullMessagePayload;
|
|
1214
1356
|
this.ttlPolicyManager = new RunMQTTLPolicyManager(logger, managementConfig);
|
|
1215
1357
|
this.metadataManager = new RunMQMetadataManager(logger, managementConfig);
|
|
1216
1358
|
}
|
|
@@ -1233,39 +1375,67 @@ var RunMQConsumerCreator = class {
|
|
|
1233
1375
|
);
|
|
1234
1376
|
}
|
|
1235
1377
|
async runProcessor(consumerConfiguration) {
|
|
1236
|
-
|
|
1378
|
+
var _a2;
|
|
1379
|
+
const consumerChannel = await this.client.getChannel({
|
|
1380
|
+
onClose: () => {
|
|
1381
|
+
if (!this.client.isActive()) {
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
this.logger.warn("Consumer channel closed; attempting to re-subscribe", {
|
|
1385
|
+
processor: consumerConfiguration.processorConfig.name
|
|
1386
|
+
});
|
|
1387
|
+
this.resubscribeProcessor(consumerConfiguration);
|
|
1388
|
+
}
|
|
1389
|
+
});
|
|
1237
1390
|
const DLQPublisher = new RunMQPublisherCreator(this.logger).createPublisher(Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME);
|
|
1238
|
-
await consumerChannel.
|
|
1391
|
+
await consumerChannel.confirmSelect();
|
|
1392
|
+
const prefetchCount = (_a2 = consumerConfiguration.processorConfig.prefetch) != null ? _a2 : DEFAULTS.PREFETCH_COUNT;
|
|
1393
|
+
await consumerChannel.prefetch(prefetchCount);
|
|
1239
1394
|
await consumerChannel.consume(consumerConfiguration.processorConfig.name, async (msg) => {
|
|
1240
|
-
if (msg)
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1395
|
+
if (!msg) return;
|
|
1396
|
+
const rabbitmqMessage = new RabbitMQMessage(
|
|
1397
|
+
msg.content.toString(),
|
|
1398
|
+
msg.properties.messageId,
|
|
1399
|
+
msg.properties.correlationId,
|
|
1400
|
+
consumerChannel,
|
|
1401
|
+
msg,
|
|
1402
|
+
msg.properties.headers
|
|
1403
|
+
);
|
|
1404
|
+
try {
|
|
1405
|
+
await new RunMQExceptionLoggerProcessor(
|
|
1250
1406
|
new RunMQSucceededMessageAcknowledgerProcessor(
|
|
1251
1407
|
new RunMQFailedMessageRejecterProcessor(
|
|
1252
1408
|
new RunMQRetriesCheckerProcessor(
|
|
1253
1409
|
new RunMQFailureLoggerProcessor(
|
|
1254
|
-
new
|
|
1255
|
-
|
|
1410
|
+
new RunMQSchemaFailureProcessor(
|
|
1411
|
+
new RunMQBaseProcessor(
|
|
1412
|
+
consumerConfiguration.processor,
|
|
1413
|
+
consumerConfiguration.processorConfig,
|
|
1414
|
+
new DefaultDeserializer()
|
|
1415
|
+
),
|
|
1256
1416
|
consumerConfiguration.processorConfig,
|
|
1257
|
-
|
|
1417
|
+
DLQPublisher,
|
|
1418
|
+
this.logger
|
|
1258
1419
|
),
|
|
1259
|
-
this.logger
|
|
1420
|
+
this.logger,
|
|
1421
|
+
this.logFullMessagePayload
|
|
1260
1422
|
),
|
|
1261
1423
|
consumerConfiguration.processorConfig,
|
|
1262
|
-
|
|
1263
|
-
this.
|
|
1264
|
-
)
|
|
1265
|
-
|
|
1424
|
+
this.logger,
|
|
1425
|
+
this.logFullMessagePayload
|
|
1426
|
+
),
|
|
1427
|
+
this.logger
|
|
1428
|
+
),
|
|
1429
|
+
this.logger
|
|
1266
1430
|
),
|
|
1267
1431
|
this.logger
|
|
1268
1432
|
).consume(rabbitmqMessage);
|
|
1433
|
+
} catch (e) {
|
|
1434
|
+
this.logger.error("Unhandled error in consumer chain", {
|
|
1435
|
+
correlationId: rabbitmqMessage.correlationId,
|
|
1436
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
1437
|
+
stack: e instanceof Error ? e.stack : void 0
|
|
1438
|
+
});
|
|
1269
1439
|
}
|
|
1270
1440
|
});
|
|
1271
1441
|
}
|
|
@@ -1334,8 +1504,20 @@ var RunMQConsumerCreator = class {
|
|
|
1334
1504
|
ConsumerCreatorUtils.getDLQTopicName(consumerConfiguration.processorConfig.name)
|
|
1335
1505
|
);
|
|
1336
1506
|
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1507
|
+
resubscribeProcessor(consumerConfiguration) {
|
|
1508
|
+
const delay = DEFAULTS.RECONNECT_DELAY;
|
|
1509
|
+
setTimeout(() => {
|
|
1510
|
+
if (!this.client.isActive()) {
|
|
1511
|
+
return;
|
|
1512
|
+
}
|
|
1513
|
+
this.runProcessor(consumerConfiguration).catch((err) => {
|
|
1514
|
+
this.logger.error("Failed to re-subscribe consumer; will retry", {
|
|
1515
|
+
processor: consumerConfiguration.processorConfig.name,
|
|
1516
|
+
error: err instanceof Error ? err.message : err
|
|
1517
|
+
});
|
|
1518
|
+
this.resubscribeProcessor(consumerConfiguration);
|
|
1519
|
+
});
|
|
1520
|
+
}, delay);
|
|
1339
1521
|
}
|
|
1340
1522
|
};
|
|
1341
1523
|
|
|
@@ -1367,7 +1549,7 @@ var RunMQ = class _RunMQ {
|
|
|
1367
1549
|
maxReconnectAttempts: (_b = config.maxReconnectAttempts) != null ? _b : DEFAULTS.MAX_RECONNECT_ATTEMPTS
|
|
1368
1550
|
});
|
|
1369
1551
|
this.client = new RabbitMQClientAdapter(this.config, this.logger);
|
|
1370
|
-
this.consumer = new RunMQConsumerCreator(this.client, this.logger, this.config.management);
|
|
1552
|
+
this.consumer = new RunMQConsumerCreator(this.client, this.logger, this.config.management, this.config.logFullMessagePayload);
|
|
1371
1553
|
}
|
|
1372
1554
|
/**
|
|
1373
1555
|
* Starts the RunMQ instance by establishing a connection to RabbitMQ and initializing necessary components.
|
|
@@ -1391,29 +1573,38 @@ var RunMQ = class _RunMQ {
|
|
|
1391
1573
|
await this.consumer.createConsumer(new ConsumerConfiguration(topic, config, processor));
|
|
1392
1574
|
}
|
|
1393
1575
|
/**
|
|
1394
|
-
* Publishes a message to the specified topic with an optional correlation ID
|
|
1576
|
+
* Publishes a message to the specified topic with an optional correlation ID.
|
|
1577
|
+
*
|
|
1578
|
+
* If publisher confirms are enabled (`usePublisherConfirms: true` in the
|
|
1579
|
+
* connection config), the returned promise resolves only after RabbitMQ
|
|
1580
|
+
* acknowledges the message; if the broker rejects, the promise rejects.
|
|
1581
|
+
* Otherwise it resolves once the message is flushed to the TCP socket
|
|
1582
|
+
* (fire-and-forget, no delivery guarantee — same behavior as before
|
|
1583
|
+
* publisher confirms were introduced).
|
|
1584
|
+
*
|
|
1395
1585
|
* @param topic The name of the topic to publish the message to
|
|
1396
1586
|
* @param message The message payload to be published
|
|
1397
1587
|
* @param correlationId (Optional) A unique identifier for correlating messages; if not provided, a new UUID will be generated
|
|
1398
1588
|
*/
|
|
1399
|
-
publish(topic, message, correlationId = RunMQUtils.generateUUID()) {
|
|
1400
|
-
if (!this.publisher || !this.
|
|
1589
|
+
async publish(topic, message, correlationId = RunMQUtils.generateUUID()) {
|
|
1590
|
+
if (!this.publisher || !this.publishChannel) {
|
|
1401
1591
|
throw new RunMQException(Exceptions.NOT_INITIALIZED, {});
|
|
1402
1592
|
}
|
|
1403
1593
|
RunMQUtils.assertRecord(message);
|
|
1404
|
-
|
|
1594
|
+
const messageId = RunMQUtils.generateUUID();
|
|
1595
|
+
await this.publisher.publish(
|
|
1405
1596
|
topic,
|
|
1406
1597
|
RabbitMQMessage.from(
|
|
1407
1598
|
message,
|
|
1408
|
-
this.
|
|
1409
|
-
new RabbitMQMessageProperties(
|
|
1599
|
+
this.publishChannel,
|
|
1600
|
+
new RabbitMQMessageProperties(messageId, correlationId)
|
|
1410
1601
|
)
|
|
1411
1602
|
);
|
|
1412
|
-
this.logger.info(`Published message`, {
|
|
1603
|
+
this.logger.info(`Published message`, __spreadValues({
|
|
1413
1604
|
topic,
|
|
1414
1605
|
correlationId,
|
|
1415
|
-
|
|
1416
|
-
});
|
|
1606
|
+
messageId
|
|
1607
|
+
}, this.config.logFullMessagePayload ? { message } : {}));
|
|
1417
1608
|
}
|
|
1418
1609
|
/**
|
|
1419
1610
|
* Disconnects from RabbitMQ, handling any errors that may occur during the disconnection process.
|
|
@@ -1466,6 +1657,10 @@ var RunMQ = class _RunMQ {
|
|
|
1466
1657
|
this.defaultChannel = await this.client.getDefaultChannel();
|
|
1467
1658
|
await this.defaultChannel.assertExchange(Constants.ROUTER_EXCHANGE_NAME, "direct", { durable: true });
|
|
1468
1659
|
await this.defaultChannel.assertExchange(Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME, "direct", { durable: true });
|
|
1660
|
+
this.publishChannel = await this.client.getChannel();
|
|
1661
|
+
if (this.config.usePublisherConfirms !== false) {
|
|
1662
|
+
await this.publishChannel.confirmSelect();
|
|
1663
|
+
}
|
|
1469
1664
|
this.publisher = new RunMQPublisherCreator(this.logger).createPublisher();
|
|
1470
1665
|
}
|
|
1471
1666
|
};
|