runmq 1.2.0 → 1.4.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/dist/index.js CHANGED
@@ -211,10 +211,39 @@ var RabbitMQClientChannel = class {
211
211
  }
212
212
  };
213
213
 
214
+ // src/core/logging/RunMQConsoleLogger.ts
215
+ var RunMQConsoleLogger = class {
216
+ constructor() {
217
+ this.prefix = "[RunMQ] - ";
218
+ }
219
+ log(message) {
220
+ console.log(this.formatMessage(message));
221
+ }
222
+ error(message, ...optionalParams) {
223
+ console.error(this.formatMessage(message), ...optionalParams);
224
+ }
225
+ warn(message, ...optionalParams) {
226
+ console.warn(this.formatMessage(message), ...optionalParams);
227
+ }
228
+ info(message, ...optionalParams) {
229
+ console.info(this.formatMessage(message), ...optionalParams);
230
+ }
231
+ debug(message, ...optionalParams) {
232
+ console.debug(this.formatMessage(message), ...optionalParams);
233
+ }
234
+ verbose(message, ...optionalParams) {
235
+ console.debug(this.formatMessage(message), ...optionalParams);
236
+ }
237
+ formatMessage(message) {
238
+ return `${this.prefix} ${message}`;
239
+ }
240
+ };
241
+
214
242
  // src/core/clients/RabbitMQClientAdapter.ts
215
243
  var RabbitMQClientAdapter = class {
216
- constructor(config) {
244
+ constructor(config, logger = new RunMQConsoleLogger()) {
217
245
  this.config = config;
246
+ this.logger = logger;
218
247
  this.isConnected = false;
219
248
  this.acquiredChannels = [];
220
249
  }
@@ -238,17 +267,17 @@ var RabbitMQClientAdapter = class {
238
267
  connectionTimeout: 5e3
239
268
  });
240
269
  this.connection.on("error", (err) => {
241
- console.error("RabbitMQ connection error:", err);
270
+ this.logger.error("RabbitMQ connection error:", { error: err });
242
271
  this.isConnected = false;
243
272
  });
244
273
  this.connection.on("connection", () => {
245
274
  this.isConnected = true;
246
275
  });
247
276
  this.connection.on("connection.blocked", (reason) => {
248
- console.warn("RabbitMQ connection blocked:", reason);
277
+ this.logger.warn("RabbitMQ connection blocked:", { reason });
249
278
  });
250
279
  this.connection.on("connection.unblocked", () => {
251
- console.info("RabbitMQ connection unblocked");
280
+ this.logger.info("RabbitMQ connection unblocked");
252
281
  });
253
282
  await this.connection.onConnect(5e3, true);
254
283
  this.isConnected = true;
@@ -335,7 +364,8 @@ var Constants = {
335
364
  DEAD_LETTER_ROUTER_EXCHANGE_NAME: RUNMQ_PREFIX + "dead_letter_router",
336
365
  RETRY_DELAY_QUEUE_PREFIX: RUNMQ_PREFIX + "retry_delay_",
337
366
  DLQ_QUEUE_PREFIX: RUNMQ_PREFIX + "dlq_",
338
- MESSAGE_TTL_OPERATOR_POLICY_PREFIX: RUNMQ_PREFIX + "message_ttl_operator_policy"
367
+ MESSAGE_TTL_OPERATOR_POLICY_PREFIX: RUNMQ_PREFIX + "message_ttl_operator_policy",
368
+ METADATA_STORE_PREFIX: RUNMQ_PREFIX + "metadata_"
339
369
  };
340
370
  var DEFAULTS = {
341
371
  RECONNECT_DELAY: 5e3,
@@ -413,6 +443,27 @@ var RunMQFailedMessageRejecterProcessor = class {
413
443
  }
414
444
  };
415
445
 
446
+ // src/core/message/RunMQMessage.ts
447
+ var RunMQMessage = class {
448
+ static isValid(obj) {
449
+ if (typeof obj === "object" && obj !== null) {
450
+ 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";
451
+ }
452
+ return false;
453
+ }
454
+ constructor(message, meta) {
455
+ this.message = message;
456
+ this.meta = meta;
457
+ }
458
+ };
459
+ var RunMQMessageMeta = class {
460
+ constructor(id, publishedAt, correlationId) {
461
+ this.id = id;
462
+ this.correlationId = correlationId;
463
+ this.publishedAt = publishedAt;
464
+ }
465
+ };
466
+
416
467
  // src/core/consumer/ConsumerCreatorUtils.ts
417
468
  var ConsumerCreatorUtils = class {
418
469
  static getDLQTopicName(topic) {
@@ -464,7 +515,28 @@ var RunMQRetriesCheckerProcessor = class {
464
515
  );
465
516
  }
466
517
  moveToFinalDeadLetter(message) {
467
- this.DLQPublisher.publish(ConsumerCreatorUtils.getDLQTopicName(this.config.name), message);
518
+ const originalPayload = this.extractOriginalPayload(message);
519
+ const dlqMessage = new RabbitMQMessage(
520
+ originalPayload,
521
+ message.id,
522
+ message.correlationId,
523
+ message.channel,
524
+ message.amqpMessage,
525
+ message.headers
526
+ );
527
+ this.DLQPublisher.publish(ConsumerCreatorUtils.getDLQTopicName(this.config.name), dlqMessage);
528
+ }
529
+ extractOriginalPayload(message) {
530
+ if (typeof message.message === "string") {
531
+ try {
532
+ const parsed = JSON.parse(message.message);
533
+ if (RunMQMessage.isValid(parsed)) {
534
+ return parsed.message;
535
+ }
536
+ } catch (e) {
537
+ }
538
+ }
539
+ return message.message;
468
540
  }
469
541
  acknowledgeMessage(message) {
470
542
  try {
@@ -542,27 +614,6 @@ var RunMQExceptionLoggerProcessor = class {
542
614
  }
543
615
  };
544
616
 
545
- // src/core/message/RunMQMessage.ts
546
- var RunMQMessage = class {
547
- static isValid(obj) {
548
- if (typeof obj === "object" && obj !== null) {
549
- 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";
550
- }
551
- return false;
552
- }
553
- constructor(message, meta) {
554
- this.message = message;
555
- this.meta = meta;
556
- }
557
- };
558
- var RunMQMessageMeta = class {
559
- constructor(id, publishedAt, correlationId) {
560
- this.id = id;
561
- this.correlationId = correlationId;
562
- this.publishedAt = publishedAt;
563
- }
564
- };
565
-
566
617
  // src/core/serializers/deserializer/validation/AjvSchemaValidator.ts
567
618
  import Ajv from "ajv";
568
619
  var AjvSchemaValidator = class {
@@ -837,6 +888,91 @@ var RabbitMQManagementClient = class {
837
888
  return false;
838
889
  }
839
890
  }
891
+ /**
892
+ * Creates or updates a RabbitMQ parameter.
893
+ * Parameters are custom key-value stores that can hold any JSON data.
894
+ *
895
+ * @param name - The parameter name
896
+ * @param value - The parameter value (any JSON-serializable object)
897
+ */
898
+ async setParameter(name, value) {
899
+ try {
900
+ const url = `${this.config.url}/api/global-parameters/${encodeURIComponent(name)}`;
901
+ const response = await fetch(url, {
902
+ method: "PUT",
903
+ headers: {
904
+ "Content-Type": "application/json",
905
+ "Authorization": this.getAuthHeader()
906
+ },
907
+ body: JSON.stringify({ value })
908
+ });
909
+ if (!response.ok) {
910
+ const error = await response.text();
911
+ this.logger.error(`Failed to set parameter ${name}: ${response.status} - ${error}`);
912
+ return false;
913
+ }
914
+ this.logger.info(`Successfully set parameter: ${name}`);
915
+ return true;
916
+ } catch (error) {
917
+ this.logger.error(`Error setting parameter: ${error}`);
918
+ return false;
919
+ }
920
+ }
921
+ /**
922
+ * Gets a RabbitMQ parameter.
923
+ *
924
+ * @param name - The parameter name
925
+ */
926
+ async getParameter(name) {
927
+ try {
928
+ const url = `${this.config.url}/api/global-parameters/${encodeURIComponent(name)}`;
929
+ const response = await fetch(url, {
930
+ method: "GET",
931
+ headers: {
932
+ "Authorization": this.getAuthHeader()
933
+ }
934
+ });
935
+ if (!response.ok) {
936
+ if (response.status === 404) {
937
+ return null;
938
+ }
939
+ const error = await response.text();
940
+ this.logger.error(`Failed to get parameter ${name}: ${response.status} - ${error}`);
941
+ return null;
942
+ }
943
+ const data = await response.json();
944
+ return data.value;
945
+ } catch (error) {
946
+ this.logger.error(`Error getting parameter: ${error}`);
947
+ return null;
948
+ }
949
+ }
950
+ /**
951
+ * Deletes a RabbitMQ parameter.
952
+ *
953
+ * @param name - The parameter name
954
+ */
955
+ async deleteParameter(name) {
956
+ try {
957
+ const url = `${this.config.url}/api/global-parameters/${encodeURIComponent(name)}`;
958
+ const response = await fetch(url, {
959
+ method: "DELETE",
960
+ headers: {
961
+ "Authorization": this.getAuthHeader()
962
+ }
963
+ });
964
+ if (!response.ok && response.status !== 404) {
965
+ const error = await response.text();
966
+ this.logger.error(`Failed to delete parameter ${name}: ${response.status} - ${error}`);
967
+ return false;
968
+ }
969
+ this.logger.info(`Successfully deleted parameter: ${name}`);
970
+ return true;
971
+ } catch (error) {
972
+ this.logger.error(`Error deleting parameter: ${error}`);
973
+ return false;
974
+ }
975
+ }
840
976
  };
841
977
 
842
978
  // src/core/management/Policies/RabbitMQMessageTTLPolicy.ts
@@ -866,6 +1002,9 @@ var RunMQTTLPolicyManager = class {
866
1002
  }
867
1003
  }
868
1004
  async initialize() {
1005
+ if (this.isManagementPluginEnabled) {
1006
+ return;
1007
+ }
869
1008
  if (!this.managementClient) {
870
1009
  this.logger.warn("Management client not configured");
871
1010
  return;
@@ -898,21 +1037,166 @@ var RunMQTTLPolicyManager = class {
898
1037
  }
899
1038
  };
900
1039
 
1040
+ // src/core/management/Policies/RunMQQueueMetadata.ts
1041
+ var METADATA_SCHEMA_VERSION = 0;
1042
+
1043
+ // src/core/management/Policies/RabbitMQMetadata.ts
1044
+ var RabbitMQMetadata = class {
1045
+ /**
1046
+ * Creates metadata for a queue.
1047
+ * @param maxRetries - Maximum retry attempts configured for the queue
1048
+ * @param existingMetadata - Optional existing metadata to preserve createdAt
1049
+ * @returns RunMQQueueMetadata object
1050
+ */
1051
+ static createMetadataFor(maxRetries, existingMetadata) {
1052
+ var _a2;
1053
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1054
+ return __spreadValues({
1055
+ version: METADATA_SCHEMA_VERSION,
1056
+ maxRetries,
1057
+ createdAt: (_a2 = existingMetadata == null ? void 0 : existingMetadata.createdAt) != null ? _a2 : now
1058
+ }, existingMetadata ? { updatedAt: now } : {});
1059
+ }
1060
+ /**
1061
+ * Gets the parameter name for a given queue.
1062
+ */
1063
+ static getParameterName(queueName) {
1064
+ return Constants.METADATA_STORE_PREFIX + queueName;
1065
+ }
1066
+ };
1067
+
1068
+ // src/core/management/Policies/RunMQMetadataManager.ts
1069
+ var RunMQMetadataManager = class {
1070
+ constructor(logger, managementConfig) {
1071
+ this.logger = logger;
1072
+ this.managementConfig = managementConfig;
1073
+ this.managementClient = null;
1074
+ this.isManagementPluginEnabled = false;
1075
+ if (this.managementConfig) {
1076
+ this.managementClient = new RabbitMQManagementClient(this.managementConfig, this.logger);
1077
+ }
1078
+ }
1079
+ /**
1080
+ * Initialize the manager by checking if management plugin is available.
1081
+ */
1082
+ async initialize() {
1083
+ if (this.isManagementPluginEnabled) {
1084
+ return;
1085
+ }
1086
+ if (!this.managementClient) {
1087
+ this.logger.warn("Management client not configured - metadata storage disabled");
1088
+ return;
1089
+ }
1090
+ this.isManagementPluginEnabled = await this.managementClient.checkManagementPluginEnabled();
1091
+ if (!this.isManagementPluginEnabled) {
1092
+ this.logger.warn("RabbitMQ management plugin is not enabled - metadata storage disabled");
1093
+ } else {
1094
+ this.logger.info("RunMQ metadata storage initialized");
1095
+ }
1096
+ }
1097
+ /**
1098
+ * Store or update metadata for a queue.
1099
+ * If metadata already exists, preserves createdAt and sets updatedAt.
1100
+ *
1101
+ * @param queueName - The name of the queue
1102
+ * @param maxRetries - Maximum retry attempts
1103
+ * @returns true if metadata was stored successfully, false otherwise
1104
+ */
1105
+ async apply(queueName, maxRetries) {
1106
+ if (!this.isManagementPluginEnabled || !this.managementClient) {
1107
+ this.logger.warn(`Cannot store metadata for queue '${queueName}' - management plugin not available`);
1108
+ return false;
1109
+ }
1110
+ try {
1111
+ const existingMetadata = await this.getMetadata(queueName);
1112
+ const metadata = RabbitMQMetadata.createMetadataFor(
1113
+ maxRetries,
1114
+ existingMetadata != null ? existingMetadata : void 0
1115
+ );
1116
+ const paramName = RabbitMQMetadata.getParameterName(queueName);
1117
+ const success = await this.managementClient.setParameter(
1118
+ paramName,
1119
+ metadata
1120
+ );
1121
+ if (success) {
1122
+ const action = existingMetadata ? "Updated" : "Created";
1123
+ this.logger.info(`${action} metadata for queue: ${queueName}`);
1124
+ return true;
1125
+ }
1126
+ this.logger.error(`Failed to store metadata for queue: ${queueName}`);
1127
+ return false;
1128
+ } catch (error) {
1129
+ this.logger.error(`Error storing metadata for queue ${queueName}: ${error}`);
1130
+ return false;
1131
+ }
1132
+ }
1133
+ /**
1134
+ * Get metadata for a queue.
1135
+ *
1136
+ * @param queueName - The name of the queue
1137
+ * @returns The queue metadata or null if not found
1138
+ */
1139
+ async getMetadata(queueName) {
1140
+ if (!this.isManagementPluginEnabled || !this.managementClient) {
1141
+ return null;
1142
+ }
1143
+ try {
1144
+ const paramName = RabbitMQMetadata.getParameterName(queueName);
1145
+ return await this.managementClient.getParameter(
1146
+ paramName
1147
+ );
1148
+ } catch (error) {
1149
+ this.logger.warn(`Failed to get metadata for queue ${queueName}: ${error}`);
1150
+ return null;
1151
+ }
1152
+ }
1153
+ /**
1154
+ * Delete metadata for a queue.
1155
+ *
1156
+ * @param queueName - The name of the queue
1157
+ */
1158
+ async cleanup(queueName) {
1159
+ if (!this.isManagementPluginEnabled || !this.managementClient) {
1160
+ return;
1161
+ }
1162
+ const paramName = RabbitMQMetadata.getParameterName(queueName);
1163
+ await this.managementClient.deleteParameter(paramName);
1164
+ this.logger.info(`Deleted metadata for queue: ${queueName}`);
1165
+ }
1166
+ /**
1167
+ * Check if management plugin is enabled and metadata storage is available.
1168
+ */
1169
+ isEnabled() {
1170
+ return this.isManagementPluginEnabled;
1171
+ }
1172
+ };
1173
+
901
1174
  // src/core/consumer/RunMQConsumerCreator.ts
902
1175
  var RunMQConsumerCreator = class {
903
1176
  constructor(client, logger, managementConfig) {
904
1177
  this.client = client;
905
1178
  this.logger = logger;
906
1179
  this.ttlPolicyManager = new RunMQTTLPolicyManager(logger, managementConfig);
1180
+ this.metadataManager = new RunMQMetadataManager(logger, managementConfig);
907
1181
  }
908
1182
  async createConsumer(consumerConfiguration) {
909
1183
  await this.ttlPolicyManager.initialize();
1184
+ await this.metadataManager.initialize();
910
1185
  await this.assertQueues(consumerConfiguration);
911
1186
  await this.bindQueues(consumerConfiguration);
1187
+ await this.storeMetadata(consumerConfiguration);
912
1188
  for (let i = 0; i < consumerConfiguration.processorConfig.consumersCount; i++) {
913
1189
  await this.runProcessor(consumerConfiguration);
914
1190
  }
915
1191
  }
1192
+ async storeMetadata(consumerConfiguration) {
1193
+ var _a2;
1194
+ const maxRetries = (_a2 = consumerConfiguration.processorConfig.attempts) != null ? _a2 : DEFAULTS.PROCESSING_ATTEMPTS;
1195
+ await this.metadataManager.apply(
1196
+ consumerConfiguration.processorConfig.name,
1197
+ maxRetries
1198
+ );
1199
+ }
916
1200
  async runProcessor(consumerConfiguration) {
917
1201
  const consumerChannel = await this.getProcessorChannel();
918
1202
  const DLQPublisher = new RunMQPublisherCreator(this.logger).createPublisher(Constants.DEAD_LETTER_ROUTER_EXCHANGE_NAME);
@@ -1029,34 +1313,6 @@ var ConsumerConfiguration = class {
1029
1313
  }
1030
1314
  };
1031
1315
 
1032
- // src/core/logging/RunMQConsoleLogger.ts
1033
- var RunMQConsoleLogger = class {
1034
- constructor() {
1035
- this.prefix = "[RunMQ] - ";
1036
- }
1037
- log(message) {
1038
- console.log(this.formatMessage(message));
1039
- }
1040
- error(message, ...optionalParams) {
1041
- console.error(this.formatMessage(message), ...optionalParams);
1042
- }
1043
- warn(message, ...optionalParams) {
1044
- console.warn(this.formatMessage(message), ...optionalParams);
1045
- }
1046
- info(message, ...optionalParams) {
1047
- console.info(this.formatMessage(message), ...optionalParams);
1048
- }
1049
- debug(message, ...optionalParams) {
1050
- console.debug(this.formatMessage(message), ...optionalParams);
1051
- }
1052
- verbose(message, ...optionalParams) {
1053
- console.debug(this.formatMessage(message), ...optionalParams);
1054
- }
1055
- formatMessage(message) {
1056
- return `${this.prefix} ${message}`;
1057
- }
1058
- };
1059
-
1060
1316
  // src/core/message/RabbitMQMessageProperties.ts
1061
1317
  var RabbitMQMessageProperties = class {
1062
1318
  constructor(id, correlationId) {
@@ -1075,7 +1331,8 @@ var RunMQ = class _RunMQ {
1075
1331
  reconnectDelay: (_a2 = config.reconnectDelay) != null ? _a2 : DEFAULTS.RECONNECT_DELAY,
1076
1332
  maxReconnectAttempts: (_b = config.maxReconnectAttempts) != null ? _b : DEFAULTS.MAX_RECONNECT_ATTEMPTS
1077
1333
  });
1078
- this.client = new RabbitMQClientAdapter(this.config);
1334
+ this.client = new RabbitMQClientAdapter(this.config, this.logger);
1335
+ this.consumer = new RunMQConsumerCreator(this.client, this.logger, this.config.management);
1079
1336
  }
1080
1337
  /**
1081
1338
  * Starts the RunMQ instance by establishing a connection to RabbitMQ and initializing necessary components.
@@ -1096,8 +1353,7 @@ var RunMQ = class _RunMQ {
1096
1353
  * @param processor The function that will process the incoming messages
1097
1354
  */
1098
1355
  async process(topic, config, processor) {
1099
- const consumer = new RunMQConsumerCreator(this.client, this.logger, this.config.management);
1100
- await consumer.createConsumer(new ConsumerConfiguration(topic, config, processor));
1356
+ await this.consumer.createConsumer(new ConsumerConfiguration(topic, config, processor));
1101
1357
  }
1102
1358
  /**
1103
1359
  * Publishes a message to the specified topic with an optional correlation ID
@@ -1156,7 +1412,6 @@ var RunMQ = class _RunMQ {
1156
1412
  return;
1157
1413
  } catch (error) {
1158
1414
  this.retryAttempts++;
1159
- console.log(this.logger);
1160
1415
  this.logger.error(`Connection attempt ${this.retryAttempts}/${maxAttempts} failed:`, error);
1161
1416
  if (this.retryAttempts >= maxAttempts) {
1162
1417
  throw new RunMQException(