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.js
CHANGED
|
@@ -124,9 +124,9 @@ var RabbitMQClientChannel = class {
|
|
|
124
124
|
arguments: args
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
|
-
publish(exchange, routingKey, content, options) {
|
|
127
|
+
async publish(exchange, routingKey, content, options) {
|
|
128
128
|
var _a2;
|
|
129
|
-
this.channel.basicPublish({
|
|
129
|
+
await this.channel.basicPublish({
|
|
130
130
|
exchange,
|
|
131
131
|
routingKey,
|
|
132
132
|
correlationId: options == null ? void 0 : options.correlationId,
|
|
@@ -143,7 +143,9 @@ var RabbitMQClientChannel = class {
|
|
|
143
143
|
userId: options == null ? void 0 : options.userId,
|
|
144
144
|
appId: options == null ? void 0 : options.appId
|
|
145
145
|
}, content);
|
|
146
|
-
|
|
146
|
+
}
|
|
147
|
+
async confirmSelect() {
|
|
148
|
+
await this.channel.confirmSelect();
|
|
147
149
|
}
|
|
148
150
|
async consume(queue, onMessage, options) {
|
|
149
151
|
const result = await this.channel.basicConsume({
|
|
@@ -246,12 +248,14 @@ var RabbitMQClientAdapter = class {
|
|
|
246
248
|
this.logger = logger;
|
|
247
249
|
this.isConnected = false;
|
|
248
250
|
this.acquiredChannels = [];
|
|
251
|
+
this.isShuttingDown = false;
|
|
249
252
|
}
|
|
250
253
|
async connect() {
|
|
251
254
|
try {
|
|
252
255
|
if (this.connection && this.isConnected) {
|
|
253
256
|
return this.connection;
|
|
254
257
|
}
|
|
258
|
+
this.isShuttingDown = false;
|
|
255
259
|
if (this.connection) {
|
|
256
260
|
try {
|
|
257
261
|
await this.connection.close();
|
|
@@ -300,21 +304,52 @@ var RabbitMQClientAdapter = class {
|
|
|
300
304
|
);
|
|
301
305
|
}
|
|
302
306
|
}
|
|
303
|
-
async getChannel() {
|
|
307
|
+
async getChannel(callbacks) {
|
|
304
308
|
const connection = await this.connect();
|
|
305
309
|
const rawChannel = await connection.acquire();
|
|
310
|
+
const channelId = rawChannel.id;
|
|
311
|
+
rawChannel.on("error", (err) => {
|
|
312
|
+
var _a2;
|
|
313
|
+
this.logger.error("RabbitMQ channel error:", { channelId, error: err });
|
|
314
|
+
try {
|
|
315
|
+
(_a2 = callbacks == null ? void 0 : callbacks.onError) == null ? void 0 : _a2.call(callbacks, err);
|
|
316
|
+
} catch (cbErr) {
|
|
317
|
+
this.logger.error("RabbitMQ channel onError callback threw:", { channelId, error: cbErr });
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
rawChannel.on("close", () => {
|
|
321
|
+
var _a2;
|
|
322
|
+
this.logger.warn("RabbitMQ channel closed", { channelId });
|
|
323
|
+
const idx = this.acquiredChannels.indexOf(rawChannel);
|
|
324
|
+
if (idx >= 0) {
|
|
325
|
+
this.acquiredChannels.splice(idx, 1);
|
|
326
|
+
}
|
|
327
|
+
if (this.isShuttingDown) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
(_a2 = callbacks == null ? void 0 : callbacks.onClose) == null ? void 0 : _a2.call(callbacks);
|
|
332
|
+
} catch (cbErr) {
|
|
333
|
+
this.logger.error("RabbitMQ channel onClose callback threw:", { channelId, error: cbErr });
|
|
334
|
+
}
|
|
335
|
+
});
|
|
306
336
|
this.acquiredChannels.push(rawChannel);
|
|
307
337
|
return new RabbitMQClientChannel(rawChannel);
|
|
308
338
|
}
|
|
309
339
|
async getDefaultChannel() {
|
|
310
340
|
if (!this.defaultChannel) {
|
|
311
|
-
this.defaultChannel = await this.getChannel(
|
|
341
|
+
this.defaultChannel = await this.getChannel({
|
|
342
|
+
onClose: () => {
|
|
343
|
+
this.defaultChannel = void 0;
|
|
344
|
+
}
|
|
345
|
+
});
|
|
312
346
|
}
|
|
313
347
|
return this.defaultChannel;
|
|
314
348
|
}
|
|
315
349
|
async disconnect() {
|
|
316
350
|
const conn = this.connection;
|
|
317
351
|
const channels = this.acquiredChannels;
|
|
352
|
+
this.isShuttingDown = true;
|
|
318
353
|
this.connection = void 0;
|
|
319
354
|
this.defaultChannel = void 0;
|
|
320
355
|
this.isConnected = false;
|
|
@@ -387,20 +422,33 @@ var RabbitMQMessage = class _RabbitMQMessage {
|
|
|
387
422
|
this.headers = headers;
|
|
388
423
|
}
|
|
389
424
|
/**
|
|
390
|
-
* Acknowledges the message.
|
|
425
|
+
* Acknowledges the message. Returns true on success, false if the
|
|
426
|
+
* underlying channel rejected the call (e.g. closed mid-flight).
|
|
427
|
+
* Plumbing errors are intentionally swallowed: the broker will redeliver
|
|
428
|
+
* unacked messages on channel close, so escalating here only crashes
|
|
429
|
+
* the consumer for no recovery benefit.
|
|
391
430
|
*/
|
|
392
431
|
ack() {
|
|
393
|
-
if (this.amqpMessage)
|
|
432
|
+
if (!this.amqpMessage) return false;
|
|
433
|
+
try {
|
|
394
434
|
this.channel.ack(this.amqpMessage);
|
|
435
|
+
return true;
|
|
436
|
+
} catch (e) {
|
|
437
|
+
return false;
|
|
395
438
|
}
|
|
396
439
|
}
|
|
397
440
|
/**
|
|
398
|
-
* Negatively acknowledges the message.
|
|
441
|
+
* Negatively acknowledges the message. Returns true on success, false if
|
|
442
|
+
* the underlying channel rejected the call. See `ack()` for rationale.
|
|
399
443
|
* @param requeue - Whether to requeue the message (default: false)
|
|
400
444
|
*/
|
|
401
445
|
nack(requeue = false) {
|
|
402
|
-
if (this.amqpMessage)
|
|
446
|
+
if (!this.amqpMessage) return false;
|
|
447
|
+
try {
|
|
403
448
|
this.channel.nack(this.amqpMessage, false, requeue);
|
|
449
|
+
return true;
|
|
450
|
+
} catch (e) {
|
|
451
|
+
return false;
|
|
404
452
|
}
|
|
405
453
|
}
|
|
406
454
|
static from(messageData, channel, props, amqpMessage = null) {
|
|
@@ -417,13 +465,20 @@ var RabbitMQMessage = class _RabbitMQMessage {
|
|
|
417
465
|
|
|
418
466
|
// src/core/consumer/processors/RunMQSucceededMessageAcknowledgerProcessor.ts
|
|
419
467
|
var RunMQSucceededMessageAcknowledgerProcessor = class {
|
|
420
|
-
constructor(consumer) {
|
|
468
|
+
constructor(consumer, logger) {
|
|
421
469
|
this.consumer = consumer;
|
|
470
|
+
this.logger = logger;
|
|
422
471
|
}
|
|
423
472
|
async consume(message) {
|
|
473
|
+
var _a2;
|
|
424
474
|
const result = await this.consumer.consume(message);
|
|
425
475
|
if (result) {
|
|
426
|
-
message.ack();
|
|
476
|
+
const acked = message.ack();
|
|
477
|
+
if (!acked) {
|
|
478
|
+
(_a2 = this.logger) == null ? void 0 : _a2.warn("Failed to ack message \u2014 channel likely closed. Broker will redeliver.", {
|
|
479
|
+
correlationId: message.correlationId
|
|
480
|
+
});
|
|
481
|
+
}
|
|
427
482
|
}
|
|
428
483
|
return result;
|
|
429
484
|
}
|
|
@@ -431,40 +486,26 @@ var RunMQSucceededMessageAcknowledgerProcessor = class {
|
|
|
431
486
|
|
|
432
487
|
// src/core/consumer/processors/RunMQFailedMessageRejecterProcessor.ts
|
|
433
488
|
var RunMQFailedMessageRejecterProcessor = class {
|
|
434
|
-
constructor(consumer) {
|
|
489
|
+
constructor(consumer, logger) {
|
|
435
490
|
this.consumer = consumer;
|
|
491
|
+
this.logger = logger;
|
|
436
492
|
}
|
|
437
493
|
async consume(message) {
|
|
494
|
+
var _a2;
|
|
438
495
|
try {
|
|
439
496
|
return await this.consumer.consume(message);
|
|
440
497
|
} catch (e) {
|
|
441
|
-
message.nack(false);
|
|
498
|
+
const nacked = message.nack(false);
|
|
499
|
+
if (!nacked) {
|
|
500
|
+
(_a2 = this.logger) == null ? void 0 : _a2.warn("Failed to nack message \u2014 channel likely closed. Broker will redeliver.", {
|
|
501
|
+
correlationId: message.correlationId
|
|
502
|
+
});
|
|
503
|
+
}
|
|
442
504
|
return false;
|
|
443
505
|
}
|
|
444
506
|
}
|
|
445
507
|
};
|
|
446
508
|
|
|
447
|
-
// src/core/message/RunMQMessage.ts
|
|
448
|
-
var RunMQMessage = class {
|
|
449
|
-
static isValid(obj) {
|
|
450
|
-
if (typeof obj === "object" && obj !== null) {
|
|
451
|
-
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";
|
|
452
|
-
}
|
|
453
|
-
return false;
|
|
454
|
-
}
|
|
455
|
-
constructor(message, meta) {
|
|
456
|
-
this.message = message;
|
|
457
|
-
this.meta = meta;
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
var RunMQMessageMeta = class {
|
|
461
|
-
constructor(id, publishedAt, correlationId) {
|
|
462
|
-
this.id = id;
|
|
463
|
-
this.correlationId = correlationId;
|
|
464
|
-
this.publishedAt = publishedAt;
|
|
465
|
-
}
|
|
466
|
-
};
|
|
467
|
-
|
|
468
509
|
// src/core/consumer/ConsumerCreatorUtils.ts
|
|
469
510
|
var ConsumerCreatorUtils = class {
|
|
470
511
|
static getDLQTopicName(topic) {
|
|
@@ -481,11 +522,11 @@ var ConsumerCreatorUtils = class {
|
|
|
481
522
|
// src/core/consumer/processors/RunMQRetriesCheckerProcessor.ts
|
|
482
523
|
var _a;
|
|
483
524
|
var RunMQRetriesCheckerProcessor = class {
|
|
484
|
-
constructor(consumer, config,
|
|
525
|
+
constructor(consumer, config, logger, logFullMessagePayload = false) {
|
|
485
526
|
this.consumer = consumer;
|
|
486
527
|
this.config = config;
|
|
487
|
-
this.DLQPublisher = DLQPublisher;
|
|
488
528
|
this.logger = logger;
|
|
529
|
+
this.logFullMessagePayload = logFullMessagePayload;
|
|
489
530
|
this.maxAttempts = (_a = this.config.attempts) != null ? _a : DEFAULTS.PROCESSING_ATTEMPTS;
|
|
490
531
|
}
|
|
491
532
|
async consume(message) {
|
|
@@ -494,7 +535,16 @@ var RunMQRetriesCheckerProcessor = class {
|
|
|
494
535
|
} catch (e) {
|
|
495
536
|
if (this.hasReachedMaxRetries(message)) {
|
|
496
537
|
this.logMaxRetriesReached(message);
|
|
497
|
-
|
|
538
|
+
try {
|
|
539
|
+
await this.moveToFinalDeadLetter(message);
|
|
540
|
+
} catch (publishError) {
|
|
541
|
+
this.logger.error("Failed to publish to DLQ \u2014 message will be redelivered", {
|
|
542
|
+
correlationId: message.correlationId,
|
|
543
|
+
cause: publishError instanceof Error ? publishError.message : String(publishError)
|
|
544
|
+
});
|
|
545
|
+
message.nack(false);
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
498
548
|
this.acknowledgeMessage(message);
|
|
499
549
|
return false;
|
|
500
550
|
}
|
|
@@ -508,44 +558,38 @@ var RunMQRetriesCheckerProcessor = class {
|
|
|
508
558
|
logMaxRetriesReached(message) {
|
|
509
559
|
this.logger.error(
|
|
510
560
|
`Message reached maximum attempts. Moving to dead-letter queue.`,
|
|
511
|
-
{
|
|
512
|
-
|
|
561
|
+
__spreadValues({
|
|
562
|
+
correlationId: message.correlationId,
|
|
563
|
+
messageId: message.id,
|
|
513
564
|
attempts: this.getRejectionCount(message),
|
|
514
565
|
max: this.maxAttempts
|
|
515
|
-
}
|
|
566
|
+
}, this.logFullMessagePayload ? { message: message.message } : {})
|
|
516
567
|
);
|
|
517
568
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const parsed = JSON.parse(message.message);
|
|
534
|
-
if (RunMQMessage.isValid(parsed)) {
|
|
535
|
-
return parsed.message;
|
|
536
|
-
}
|
|
537
|
-
} catch (e) {
|
|
569
|
+
// Republish the original AMQP body verbatim so the envelope (including
|
|
570
|
+
// publishedAt) is preserved end-to-end for audit/replay. The consumer
|
|
571
|
+
// channel runs in confirm mode (see RunMQConsumerCreator), so awaiting
|
|
572
|
+
// this publish surfaces broker-side rejections instead of dropping them.
|
|
573
|
+
async moveToFinalDeadLetter(message) {
|
|
574
|
+
if (!message.amqpMessage) return;
|
|
575
|
+
await message.channel.publish(
|
|
576
|
+
Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME,
|
|
577
|
+
ConsumerCreatorUtils.getDLQTopicName(this.config.name),
|
|
578
|
+
message.amqpMessage.content,
|
|
579
|
+
{
|
|
580
|
+
correlationId: message.correlationId,
|
|
581
|
+
messageId: message.id,
|
|
582
|
+
headers: message.headers,
|
|
583
|
+
persistent: true
|
|
538
584
|
}
|
|
539
|
-
|
|
540
|
-
return message.message;
|
|
585
|
+
);
|
|
541
586
|
}
|
|
542
587
|
acknowledgeMessage(message) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
throw error;
|
|
588
|
+
const acked = message.ack();
|
|
589
|
+
if (!acked) {
|
|
590
|
+
this.logger.warn("Failed to ack message after publishing to final dead letter \u2014 channel likely closed. Broker will redeliver.", {
|
|
591
|
+
correlationId: message.correlationId
|
|
592
|
+
});
|
|
549
593
|
}
|
|
550
594
|
}
|
|
551
595
|
getRejectionCount(message) {
|
|
@@ -559,9 +603,10 @@ var RunMQRetriesCheckerProcessor = class {
|
|
|
559
603
|
|
|
560
604
|
// src/core/consumer/processors/RunMQFailureLoggerProcessor.ts
|
|
561
605
|
var RunMQFailureLoggerProcessor = class {
|
|
562
|
-
constructor(consumer, logger) {
|
|
606
|
+
constructor(consumer, logger, logFullMessagePayload = false) {
|
|
563
607
|
this.consumer = consumer;
|
|
564
608
|
this.logger = logger;
|
|
609
|
+
this.logFullMessagePayload = logFullMessagePayload;
|
|
565
610
|
}
|
|
566
611
|
async consume(message) {
|
|
567
612
|
try {
|
|
@@ -569,9 +614,10 @@ var RunMQFailureLoggerProcessor = class {
|
|
|
569
614
|
} catch (e) {
|
|
570
615
|
this.logger.error(
|
|
571
616
|
"Message processing failed",
|
|
572
|
-
{
|
|
573
|
-
|
|
574
|
-
|
|
617
|
+
__spreadValues({
|
|
618
|
+
correlationId: message.correlationId,
|
|
619
|
+
messageId: message.id
|
|
620
|
+
}, this.logFullMessagePayload ? { message: message.message } : {}),
|
|
575
621
|
e instanceof Error ? e.stack : void 0
|
|
576
622
|
);
|
|
577
623
|
throw e;
|
|
@@ -579,39 +625,24 @@ var RunMQFailureLoggerProcessor = class {
|
|
|
579
625
|
}
|
|
580
626
|
};
|
|
581
627
|
|
|
582
|
-
// src/core/
|
|
583
|
-
var
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
628
|
+
// src/core/message/RunMQMessage.ts
|
|
629
|
+
var RunMQMessage = class {
|
|
630
|
+
static isValid(obj) {
|
|
631
|
+
if (typeof obj === "object" && obj !== null) {
|
|
632
|
+
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";
|
|
633
|
+
}
|
|
634
|
+
return false;
|
|
588
635
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return true;
|
|
636
|
+
constructor(message, meta) {
|
|
637
|
+
this.message = message;
|
|
638
|
+
this.meta = meta;
|
|
593
639
|
}
|
|
594
640
|
};
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
this.
|
|
600
|
-
this.logger = logger;
|
|
601
|
-
}
|
|
602
|
-
async consume(message) {
|
|
603
|
-
try {
|
|
604
|
-
return await this.consumer.consume(message);
|
|
605
|
-
} catch (e) {
|
|
606
|
-
if (e instanceof Error) {
|
|
607
|
-
this.logger.error(e.message, e.stack);
|
|
608
|
-
throw e;
|
|
609
|
-
} else {
|
|
610
|
-
const errorString = JSON.stringify(e);
|
|
611
|
-
this.logger.error(errorString);
|
|
612
|
-
throw new Error(errorString);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
641
|
+
var RunMQMessageMeta = class {
|
|
642
|
+
constructor(id, publishedAt, correlationId) {
|
|
643
|
+
this.id = id;
|
|
644
|
+
this.correlationId = correlationId;
|
|
645
|
+
this.publishedAt = publishedAt;
|
|
615
646
|
}
|
|
616
647
|
};
|
|
617
648
|
|
|
@@ -620,6 +651,17 @@ import Ajv from "ajv";
|
|
|
620
651
|
var AjvSchemaValidator = class {
|
|
621
652
|
constructor() {
|
|
622
653
|
this.lastValidator = null;
|
|
654
|
+
/**
|
|
655
|
+
* Cache of compiled validators, keyed by schema identity.
|
|
656
|
+
*
|
|
657
|
+
* `ajv.compile()` codegens an optimized JS function from the schema
|
|
658
|
+
* (typically 2-10ms for non-trivial schemas). Without this cache the
|
|
659
|
+
* compile would run on every message — at high throughput it dominates
|
|
660
|
+
* CPU usage.
|
|
661
|
+
*
|
|
662
|
+
* WeakMap so we don't pin schemas in memory if a processor is removed.
|
|
663
|
+
*/
|
|
664
|
+
this.compiled = /* @__PURE__ */ new WeakMap();
|
|
623
665
|
this.ajv = new Ajv({
|
|
624
666
|
allErrors: true,
|
|
625
667
|
verbose: true,
|
|
@@ -627,8 +669,14 @@ var AjvSchemaValidator = class {
|
|
|
627
669
|
});
|
|
628
670
|
}
|
|
629
671
|
validate(schema, data) {
|
|
630
|
-
|
|
631
|
-
|
|
672
|
+
const key = schema;
|
|
673
|
+
let validator = this.compiled.get(key);
|
|
674
|
+
if (!validator) {
|
|
675
|
+
validator = this.ajv.compile(schema);
|
|
676
|
+
this.compiled.set(key, validator);
|
|
677
|
+
}
|
|
678
|
+
this.lastValidator = validator;
|
|
679
|
+
return validator(data);
|
|
632
680
|
}
|
|
633
681
|
getError() {
|
|
634
682
|
if (!this.lastValidator || !this.lastValidator.errors) {
|
|
@@ -714,18 +762,111 @@ var DefaultDeserializer = class {
|
|
|
714
762
|
}
|
|
715
763
|
};
|
|
716
764
|
|
|
765
|
+
// src/core/consumer/processors/RunMQSchemaFailureProcessor.ts
|
|
766
|
+
var RunMQSchemaFailureProcessor = class {
|
|
767
|
+
constructor(consumer, config, DLQPublisher, logger) {
|
|
768
|
+
this.consumer = consumer;
|
|
769
|
+
this.config = config;
|
|
770
|
+
this.DLQPublisher = DLQPublisher;
|
|
771
|
+
this.logger = logger;
|
|
772
|
+
}
|
|
773
|
+
async consume(message) {
|
|
774
|
+
try {
|
|
775
|
+
return await this.consumer.consume(message);
|
|
776
|
+
} catch (e) {
|
|
777
|
+
if (this.shouldRouteToDLQ(e)) {
|
|
778
|
+
this.logger.warn("Schema validation failed \u2014 routing message to DLQ.", {
|
|
779
|
+
correlationId: message.correlationId,
|
|
780
|
+
error: e instanceof RunMQSchemaValidationError ? e.error : void 0
|
|
781
|
+
});
|
|
782
|
+
this.routeToDLQ(message);
|
|
783
|
+
return true;
|
|
784
|
+
}
|
|
785
|
+
throw e;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
shouldRouteToDLQ(e) {
|
|
789
|
+
var _a2;
|
|
790
|
+
if (!(e instanceof RunMQSchemaValidationError)) return false;
|
|
791
|
+
return ((_a2 = this.config.messageSchema) == null ? void 0 : _a2.failureStrategy) === "dlq";
|
|
792
|
+
}
|
|
793
|
+
routeToDLQ(message) {
|
|
794
|
+
const dlqMessage = new RabbitMQMessage(
|
|
795
|
+
this.extractOriginalPayload(message),
|
|
796
|
+
message.id,
|
|
797
|
+
message.correlationId,
|
|
798
|
+
message.channel,
|
|
799
|
+
message.amqpMessage,
|
|
800
|
+
message.headers
|
|
801
|
+
);
|
|
802
|
+
this.DLQPublisher.publish(
|
|
803
|
+
ConsumerCreatorUtils.getDLQTopicName(this.config.name),
|
|
804
|
+
dlqMessage
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
extractOriginalPayload(message) {
|
|
808
|
+
if (typeof message.message === "string") {
|
|
809
|
+
try {
|
|
810
|
+
const parsed = JSON.parse(message.message);
|
|
811
|
+
if (RunMQMessage.isValid(parsed)) {
|
|
812
|
+
return parsed.message;
|
|
813
|
+
}
|
|
814
|
+
} catch (e) {
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return message.message;
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
|
|
821
|
+
// src/core/consumer/processors/RunMQBaseProcessor.ts
|
|
822
|
+
var RunMQBaseProcessor = class {
|
|
823
|
+
constructor(handler, processorConfig, serializer) {
|
|
824
|
+
this.handler = handler;
|
|
825
|
+
this.processorConfig = processorConfig;
|
|
826
|
+
this.serializer = serializer;
|
|
827
|
+
}
|
|
828
|
+
async consume(message) {
|
|
829
|
+
const rabbitMQMessage = this.serializer.deserialize(message.message, this.processorConfig);
|
|
830
|
+
await this.handler(rabbitMQMessage);
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
|
|
835
|
+
// src/core/consumer/processors/RunMQExceptionLoggerProcessor.ts
|
|
836
|
+
var RunMQExceptionLoggerProcessor = class {
|
|
837
|
+
constructor(consumer, logger) {
|
|
838
|
+
this.consumer = consumer;
|
|
839
|
+
this.logger = logger;
|
|
840
|
+
}
|
|
841
|
+
async consume(message) {
|
|
842
|
+
try {
|
|
843
|
+
return await this.consumer.consume(message);
|
|
844
|
+
} catch (e) {
|
|
845
|
+
if (e instanceof Error) {
|
|
846
|
+
this.logger.error(e.message, e.stack);
|
|
847
|
+
throw e;
|
|
848
|
+
} else {
|
|
849
|
+
const errorString = JSON.stringify(e);
|
|
850
|
+
this.logger.error(errorString);
|
|
851
|
+
throw new Error(errorString);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
};
|
|
856
|
+
|
|
717
857
|
// src/core/publisher/producers/RunMQFailureLoggerProducer.ts
|
|
718
858
|
var RunMQFailureLoggerProducer = class {
|
|
719
859
|
constructor(producer, logger) {
|
|
720
860
|
this.producer = producer;
|
|
721
861
|
this.logger = logger;
|
|
722
862
|
}
|
|
723
|
-
publish(topic, message) {
|
|
863
|
+
async publish(topic, message) {
|
|
724
864
|
try {
|
|
725
|
-
this.producer.publish(topic, message);
|
|
865
|
+
await this.producer.publish(topic, message);
|
|
726
866
|
} catch (e) {
|
|
727
867
|
this.logger.error("Message publishing failed", {
|
|
728
|
-
|
|
868
|
+
topic,
|
|
869
|
+
correlationId: message.correlationId,
|
|
729
870
|
error: e instanceof Error ? e.message : JSON.stringify(e),
|
|
730
871
|
stack: e instanceof Error ? e.stack : void 0
|
|
731
872
|
});
|
|
@@ -740,7 +881,7 @@ var RunMQBaseProducer = class {
|
|
|
740
881
|
this.serializer = serializer;
|
|
741
882
|
this.exchange = exchange;
|
|
742
883
|
}
|
|
743
|
-
publish(topic, message) {
|
|
884
|
+
async publish(topic, message) {
|
|
744
885
|
const runMQMessage = new RunMQMessage(
|
|
745
886
|
message.message,
|
|
746
887
|
new RunMQMessageMeta(
|
|
@@ -750,7 +891,7 @@ var RunMQBaseProducer = class {
|
|
|
750
891
|
)
|
|
751
892
|
);
|
|
752
893
|
const serialized = this.serializer.serialize(runMQMessage);
|
|
753
|
-
message.channel.publish(this.exchange, topic, Buffer.from(serialized), {
|
|
894
|
+
await message.channel.publish(this.exchange, topic, Buffer.from(serialized), {
|
|
754
895
|
correlationId: message.correlationId,
|
|
755
896
|
messageId: message.id,
|
|
756
897
|
headers: message.headers,
|
|
@@ -1175,9 +1316,10 @@ var RunMQMetadataManager = class {
|
|
|
1175
1316
|
|
|
1176
1317
|
// src/core/consumer/RunMQConsumerCreator.ts
|
|
1177
1318
|
var RunMQConsumerCreator = class {
|
|
1178
|
-
constructor(client, logger, managementConfig) {
|
|
1319
|
+
constructor(client, logger, managementConfig, logFullMessagePayload = false) {
|
|
1179
1320
|
this.client = client;
|
|
1180
1321
|
this.logger = logger;
|
|
1322
|
+
this.logFullMessagePayload = logFullMessagePayload;
|
|
1181
1323
|
this.ttlPolicyManager = new RunMQTTLPolicyManager(logger, managementConfig);
|
|
1182
1324
|
this.metadataManager = new RunMQMetadataManager(logger, managementConfig);
|
|
1183
1325
|
}
|
|
@@ -1200,39 +1342,67 @@ var RunMQConsumerCreator = class {
|
|
|
1200
1342
|
);
|
|
1201
1343
|
}
|
|
1202
1344
|
async runProcessor(consumerConfiguration) {
|
|
1203
|
-
|
|
1345
|
+
var _a2;
|
|
1346
|
+
const consumerChannel = await this.client.getChannel({
|
|
1347
|
+
onClose: () => {
|
|
1348
|
+
if (!this.client.isActive()) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
this.logger.warn("Consumer channel closed; attempting to re-subscribe", {
|
|
1352
|
+
processor: consumerConfiguration.processorConfig.name
|
|
1353
|
+
});
|
|
1354
|
+
this.resubscribeProcessor(consumerConfiguration);
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1204
1357
|
const DLQPublisher = new RunMQPublisherCreator(this.logger).createPublisher(Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME);
|
|
1205
|
-
await consumerChannel.
|
|
1358
|
+
await consumerChannel.confirmSelect();
|
|
1359
|
+
const prefetchCount = (_a2 = consumerConfiguration.processorConfig.prefetch) != null ? _a2 : DEFAULTS.PREFETCH_COUNT;
|
|
1360
|
+
await consumerChannel.prefetch(prefetchCount);
|
|
1206
1361
|
await consumerChannel.consume(consumerConfiguration.processorConfig.name, async (msg) => {
|
|
1207
|
-
if (msg)
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1362
|
+
if (!msg) return;
|
|
1363
|
+
const rabbitmqMessage = new RabbitMQMessage(
|
|
1364
|
+
msg.content.toString(),
|
|
1365
|
+
msg.properties.messageId,
|
|
1366
|
+
msg.properties.correlationId,
|
|
1367
|
+
consumerChannel,
|
|
1368
|
+
msg,
|
|
1369
|
+
msg.properties.headers
|
|
1370
|
+
);
|
|
1371
|
+
try {
|
|
1372
|
+
await new RunMQExceptionLoggerProcessor(
|
|
1217
1373
|
new RunMQSucceededMessageAcknowledgerProcessor(
|
|
1218
1374
|
new RunMQFailedMessageRejecterProcessor(
|
|
1219
1375
|
new RunMQRetriesCheckerProcessor(
|
|
1220
1376
|
new RunMQFailureLoggerProcessor(
|
|
1221
|
-
new
|
|
1222
|
-
|
|
1377
|
+
new RunMQSchemaFailureProcessor(
|
|
1378
|
+
new RunMQBaseProcessor(
|
|
1379
|
+
consumerConfiguration.processor,
|
|
1380
|
+
consumerConfiguration.processorConfig,
|
|
1381
|
+
new DefaultDeserializer()
|
|
1382
|
+
),
|
|
1223
1383
|
consumerConfiguration.processorConfig,
|
|
1224
|
-
|
|
1384
|
+
DLQPublisher,
|
|
1385
|
+
this.logger
|
|
1225
1386
|
),
|
|
1226
|
-
this.logger
|
|
1387
|
+
this.logger,
|
|
1388
|
+
this.logFullMessagePayload
|
|
1227
1389
|
),
|
|
1228
1390
|
consumerConfiguration.processorConfig,
|
|
1229
|
-
|
|
1230
|
-
this.
|
|
1231
|
-
)
|
|
1232
|
-
|
|
1391
|
+
this.logger,
|
|
1392
|
+
this.logFullMessagePayload
|
|
1393
|
+
),
|
|
1394
|
+
this.logger
|
|
1395
|
+
),
|
|
1396
|
+
this.logger
|
|
1233
1397
|
),
|
|
1234
1398
|
this.logger
|
|
1235
1399
|
).consume(rabbitmqMessage);
|
|
1400
|
+
} catch (e) {
|
|
1401
|
+
this.logger.error("Unhandled error in consumer chain", {
|
|
1402
|
+
correlationId: rabbitmqMessage.correlationId,
|
|
1403
|
+
cause: e instanceof Error ? e.message : String(e),
|
|
1404
|
+
stack: e instanceof Error ? e.stack : void 0
|
|
1405
|
+
});
|
|
1236
1406
|
}
|
|
1237
1407
|
});
|
|
1238
1408
|
}
|
|
@@ -1301,8 +1471,20 @@ var RunMQConsumerCreator = class {
|
|
|
1301
1471
|
ConsumerCreatorUtils.getDLQTopicName(consumerConfiguration.processorConfig.name)
|
|
1302
1472
|
);
|
|
1303
1473
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1474
|
+
resubscribeProcessor(consumerConfiguration) {
|
|
1475
|
+
const delay = DEFAULTS.RECONNECT_DELAY;
|
|
1476
|
+
setTimeout(() => {
|
|
1477
|
+
if (!this.client.isActive()) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
this.runProcessor(consumerConfiguration).catch((err) => {
|
|
1481
|
+
this.logger.error("Failed to re-subscribe consumer; will retry", {
|
|
1482
|
+
processor: consumerConfiguration.processorConfig.name,
|
|
1483
|
+
error: err instanceof Error ? err.message : err
|
|
1484
|
+
});
|
|
1485
|
+
this.resubscribeProcessor(consumerConfiguration);
|
|
1486
|
+
});
|
|
1487
|
+
}, delay);
|
|
1306
1488
|
}
|
|
1307
1489
|
};
|
|
1308
1490
|
|
|
@@ -1334,7 +1516,7 @@ var RunMQ = class _RunMQ {
|
|
|
1334
1516
|
maxReconnectAttempts: (_b = config.maxReconnectAttempts) != null ? _b : DEFAULTS.MAX_RECONNECT_ATTEMPTS
|
|
1335
1517
|
});
|
|
1336
1518
|
this.client = new RabbitMQClientAdapter(this.config, this.logger);
|
|
1337
|
-
this.consumer = new RunMQConsumerCreator(this.client, this.logger, this.config.management);
|
|
1519
|
+
this.consumer = new RunMQConsumerCreator(this.client, this.logger, this.config.management, this.config.logFullMessagePayload);
|
|
1338
1520
|
}
|
|
1339
1521
|
/**
|
|
1340
1522
|
* Starts the RunMQ instance by establishing a connection to RabbitMQ and initializing necessary components.
|
|
@@ -1358,29 +1540,38 @@ var RunMQ = class _RunMQ {
|
|
|
1358
1540
|
await this.consumer.createConsumer(new ConsumerConfiguration(topic, config, processor));
|
|
1359
1541
|
}
|
|
1360
1542
|
/**
|
|
1361
|
-
* Publishes a message to the specified topic with an optional correlation ID
|
|
1543
|
+
* Publishes a message to the specified topic with an optional correlation ID.
|
|
1544
|
+
*
|
|
1545
|
+
* If publisher confirms are enabled (`usePublisherConfirms: true` in the
|
|
1546
|
+
* connection config), the returned promise resolves only after RabbitMQ
|
|
1547
|
+
* acknowledges the message; if the broker rejects, the promise rejects.
|
|
1548
|
+
* Otherwise it resolves once the message is flushed to the TCP socket
|
|
1549
|
+
* (fire-and-forget, no delivery guarantee — same behavior as before
|
|
1550
|
+
* publisher confirms were introduced).
|
|
1551
|
+
*
|
|
1362
1552
|
* @param topic The name of the topic to publish the message to
|
|
1363
1553
|
* @param message The message payload to be published
|
|
1364
1554
|
* @param correlationId (Optional) A unique identifier for correlating messages; if not provided, a new UUID will be generated
|
|
1365
1555
|
*/
|
|
1366
|
-
publish(topic, message, correlationId = RunMQUtils.generateUUID()) {
|
|
1367
|
-
if (!this.publisher || !this.
|
|
1556
|
+
async publish(topic, message, correlationId = RunMQUtils.generateUUID()) {
|
|
1557
|
+
if (!this.publisher || !this.publishChannel) {
|
|
1368
1558
|
throw new RunMQException(Exceptions.NOT_INITIALIZED, {});
|
|
1369
1559
|
}
|
|
1370
1560
|
RunMQUtils.assertRecord(message);
|
|
1371
|
-
|
|
1561
|
+
const messageId = RunMQUtils.generateUUID();
|
|
1562
|
+
await this.publisher.publish(
|
|
1372
1563
|
topic,
|
|
1373
1564
|
RabbitMQMessage.from(
|
|
1374
1565
|
message,
|
|
1375
|
-
this.
|
|
1376
|
-
new RabbitMQMessageProperties(
|
|
1566
|
+
this.publishChannel,
|
|
1567
|
+
new RabbitMQMessageProperties(messageId, correlationId)
|
|
1377
1568
|
)
|
|
1378
1569
|
);
|
|
1379
|
-
this.logger.info(`Published message`, {
|
|
1570
|
+
this.logger.info(`Published message`, __spreadValues({
|
|
1380
1571
|
topic,
|
|
1381
1572
|
correlationId,
|
|
1382
|
-
|
|
1383
|
-
});
|
|
1573
|
+
messageId
|
|
1574
|
+
}, this.config.logFullMessagePayload ? { message } : {}));
|
|
1384
1575
|
}
|
|
1385
1576
|
/**
|
|
1386
1577
|
* Disconnects from RabbitMQ, handling any errors that may occur during the disconnection process.
|
|
@@ -1433,6 +1624,10 @@ var RunMQ = class _RunMQ {
|
|
|
1433
1624
|
this.defaultChannel = await this.client.getDefaultChannel();
|
|
1434
1625
|
await this.defaultChannel.assertExchange(Constants.ROUTER_EXCHANGE_NAME, "direct", { durable: true });
|
|
1435
1626
|
await this.defaultChannel.assertExchange(Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME, "direct", { durable: true });
|
|
1627
|
+
this.publishChannel = await this.client.getChannel();
|
|
1628
|
+
if (this.config.usePublisherConfirms !== false) {
|
|
1629
|
+
await this.publishChannel.confirmSelect();
|
|
1630
|
+
}
|
|
1436
1631
|
this.publisher = new RunMQPublisherCreator(this.logger).createPublisher();
|
|
1437
1632
|
}
|
|
1438
1633
|
};
|