legend-transactional
Version:
A simple transactional, event-driven communication framework for microservices using RabbitMQ
1,489 lines (1,457 loc) • 69.7 kB
text/typescript
import { Channel, ConsumeMessage, ChannelModel } from 'amqplib';
import { Emitter, EventType } from 'mitt';
/**
* Represents the available microservices in the system.
* The names are taken from the repository names in GitHub: https://github.com/orgs/legendaryum-metaverse/repositories?type=all
*/
declare const availableMicroservices: {
/**
* Test purpose
* Represents the "Image" test microservice.
*/
readonly TestImage: "test-image";
/**
* Test purpose
* Represents the "Mint" test microservice.
*/
readonly TestMint: "test-mint";
/**
* Represents the "audit-eda" microservice for event-driven architecture auditing.
* This microservice consumes audit events (audit.received, audit.processed, audit.dead_letter)
* to track event lifecycle and debugging purposes.
*/
readonly AuditEda: "audit-eda";
/**
* Represents the "auth" microservice.
*/
readonly Auth: "auth";
/**
* Represents the "legend-billing" microservice.
* Handles payment processing, subscriptions, and billing domain events.
*/
readonly Billing: "billing";
/**
* Represents the "blockchain" microservice.
*/
readonly Blockchain: "blockchain";
/**
* Represents the "legend-missions" microservice.
*/
readonly Missions: "legend-missions";
/**
* Represents the "rankings" microservice.
*/
readonly Rankings: "rankings";
/**
* Represents the "legend-events" microservice.
*/
readonly Events: "legend-events";
/**
* Represents the "transactional" microservice.
*/
readonly Transactional: "transactional";
/**
* Represents the "legend-send-email" microservice.
*/
readonly SendEmail: "legend-send-email";
/**
* Represents the "legend-showcase" microservice.
*/
readonly Showcase: "legend-showcase";
/**
* Represents the "social" microservice.
*/
readonly Social: "social";
/**
* Represents the "legend-storage" microservice.
*/
readonly Storage: "legend-storage";
/**
* Represents the "legend-game-analytics" microservice.
*/
readonly LegendGameAnalytics: "legend-game-analytics";
};
/**
* Type of available microservices in the system.
*/
type AvailableMicroservices = (typeof availableMicroservices)[keyof typeof availableMicroservices];
/**
* Represents the winners of a ranking with their respective rewards
*/
interface RankingWinners {
userId: string;
reward: string;
}
/**
* Represents a completed ranking with its title, reward type, and winners
*/
interface CompletedRanking {
title: string;
description: string;
authorEmail: string;
/**
* End date converted to string
*/
endsAt: string;
/**
* JSON stringified with each user's rewards
*/
reward: string;
rewardType: string;
winners: RankingWinners[];
nftBlockchainNetwork?: string;
nftContractAddress?: string;
walletCryptoAsset?: string;
/** Optional notification config forwarded from rankings (dynamic template data) */
notificationConfig?: Record<string, unknown> & {
button_link?: string;
button_text?: string;
};
}
/**
* Represents the possible genders a social user can have
*/
declare const gender: {
readonly Male: "MALE";
readonly Female: "FEMALE";
readonly Undefined: "UNDEFINED";
};
/**
* The `Gender` type is derived from the keys of the `gender`
*/
type Gender = (typeof gender)[keyof typeof gender];
/**
* Representes the user location
*/
interface UserLocation {
continent: string;
country: string;
region: string;
city: string;
}
/**
* Represents the social user model
*/
interface SocialUser {
_id: string;
username: string;
firstName?: string;
lastName?: string;
gender: Gender;
isPublicProfile?: boolean;
followers: string[];
following: string[];
email: string;
birthday?: Date;
location?: UserLocation;
avatar?: string;
avatarScreenshot?: string;
userImage?: string;
glbUrl?: string;
description?: string;
socialMedia?: Map<string, string>;
preferences: string[];
blockedUsers: string[];
RPMAvatarId?: string;
RPMUserId?: string;
paidPriceId?: string;
createdAt: Date;
}
/**
* Represents the available event's payload in the system.
*/
interface EventPayload {
/**
* Test purpose
*
* Event broadcast by the "Image" test microservice.
* @internal
*/
'test.image': {
image: string;
};
/**
* Test purpose
*
* Event broadcast by the "Mint" test microservice.
* @internal
*/
'test.mint': {
mint: string;
};
/**
* Event to notify the deletion of a user.
*/
'auth.deleted_user': {
userId: string;
};
/**
* Event to logout a user.
*/
'auth.logout_user': {
userId: string;
};
/**
* Event to duplicate minimal user data in the microservice that listens to it. This occurs when the user is created.
*/
'auth.new_user': {
id: string;
email: string;
username: string;
userlastname: string;
};
/**
* Event to notify when a user is blocked (permanent emits only).
*/
'auth.blocked_user': {
userId: string;
blockType: 'permanent' | 'temporary';
blockReason?: string;
blockExpirationHours?: number;
};
/**
* Event to notify the mission's author that it has been created
*/
'legend_missions.new_mission_created': {
title: string;
author: string;
authorEmail: string;
reward: number;
startDate: string;
endDate: string;
maxPlayersClaimingReward: number;
timeToReward: number;
notificationConfig?: {
customEmails?: string[];
templateName: string;
};
};
/**
* Event to set a mission in progress
*/
'legend_missions.ongoing_mission': {
redisKey: string;
};
/**
* Event triggered when a mission finishes and needs to send final reports to participants
*/
'legend_missions.mission_finished': {
missionTitle: string;
participants: Array<{
userId?: string;
email?: string;
position?: number;
}>;
};
/**
* Event triggered to send an email notification when a user completes a crypto-based mission and earns a reward.
*/
'legend_missions.send_email_crypto_mission_completed': {
userId: string;
missionTitle: string;
reward: string;
blockchainNetwork: string;
cryptoAsset: string;
};
/**
* Event triggered to send an email notification when a user redeems a code and completes a mission.
*/
'legend_missions.send_email_code_exchange_mission_completed': {
userId: string;
missionTitle: string;
codeValue: string;
codeDescription: string;
};
/**
* Event triggered to send an email notification when a user completes an NFT-related mission.
*/
'legend_missions.send_email_nft_mission_completed': {
userId: string;
missionTitle: string;
nftContractAddress: string;
nftTokenId: string;
};
/**
* Event to send emails to winners when the ranking finishes
*/
'legend_rankings.rankings_finished': {
completedRankings: CompletedRanking[];
};
/**
* Event to deliver intermediate reward (e.g., first game)
*/
'legend_rankings.intermediate_reward': {
userId: string;
rankingId: number;
intermediateRewardType: string;
rewardConfig: Record<string, unknown>;
templateName: string;
templateData: Record<string, unknown>;
};
/**
* Event to deliver participation reward (post-ranking)
*/
'legend_rankings.participation_reward': {
userId: string;
rankingId: number;
participationRewardType: string;
rewardConfig: Record<string, unknown>;
templateName: string;
templateData: Record<string, unknown>;
};
/**
* Event to notify when a ranking is created
*/
'legend_rankings.new_ranking_created': {
title: string;
description: string;
authorEmail: string;
rewardType: string;
startAt: string;
endsAt: string;
nftBlockchainNetwork?: string;
nftContractAddress?: string;
walletCryptoAsset?: string;
notificationConfig?: {
customEmails?: string[];
templateName: string;
};
};
/**
* Event to notify when a ranking is submitted for review
*/
'legend_rankings.ranking_submitted_for_review': {
rankingId: number;
title: string;
authorEmail: string;
createdAt: string;
};
/**
* Event to notify when a ranking is approved
*/
'legend_rankings.ranking_approved': {
rankingId: number;
title: string;
authorEmail: string;
startAt: string;
};
/**
* Event to notify when a ranking is rejected
*/
'legend_rankings.ranking_rejected': {
rankingId: number;
title: string;
authorEmail: string;
reasons: string;
};
/**
* Event to notify when a ranking is activated
*/
'legend_rankings.ranking_activated': {
rankingId: number;
title: string;
authorEmail: string;
startAt: string;
};
/**
* Event triggered when a product virtual is deleted
*/
'legend_showcase.product_virtual_deleted': {
productVirtualId: string;
productVirtualSlug: string;
};
/**
* Event to update the allowed mission subscription IDs
*/
'legend_showcase.update_allowed_mission_subscription_ids': {
productVirtualSlug: string;
allowedSubscriptionIds: string[];
};
/**
* Event to update the allowed ranking subscription IDs
*/
'legend_showcase.update_allowed_ranking_subscription_ids': {
productVirtualId: string;
allowedSubscriptionIds: string[];
};
/**
* Event to block chat between two users.
*/
'social.block_chat': {
userId: string;
userToBlockId: string;
};
/**
* New user in social table in social microservice
*/
'social.new_user': {
socialUser: SocialUser;
};
/**
* Event to unblock chat between two users.
*/
'social.unblock_chat': {
userId: string;
userToUnblockId: string;
};
/**
* A social user has been updated
*/
'social.updated_user': {
socialUser: SocialUser;
};
/**
* Emitted when an event is published by a microservice (audit tracking)
*/
'audit.published': {
/**
* The microservice that published the event
*/
publisher_microservice: string;
/**
* The event that was published
*/
published_event: string;
/**
* Timestamp when the event was published (UNIX timestamp in milliseconds)
*/
published_at: number;
/**
* UUID v7 unique identifier for cross-event tracking
*/
event_id: string;
};
/**
* Emitted when an event is received by a microservice before processing starts (audit tracking)
*/
'audit.received': {
/**
* The microservice that published the original event
*/
publisher_microservice: string;
/**
* The microservice that received the event
*/
receiver_microservice: string;
/**
* The event that was received
*/
received_event: string;
/**
* Timestamp when the event was received (UNIX timestamp in milliseconds)
*/
received_at: number;
/**
* The queue name from which the event was consumed
*/
queue_name: string;
/**
* UUID v7 from message properties for cross-event tracking
*/
event_id: string;
};
/**
* Emitted when an event is successfully processed by a microservice for audit tracking
*/
'audit.processed': {
/**
* The microservice that published the original event
*/
publisher_microservice: string;
/**
* The microservice that processed the event
*/
processor_microservice: string;
/**
* The original event that was processed
*/
processed_event: string;
/**
* Timestamp when the event was processed (UNIX timestamp in milliseconds)
*/
processed_at: number;
/**
* The queue name where the event was consumed
*/
queue_name: string;
/**
* UUID v7 from message properties for cross-event tracking
*/
event_id: string;
};
/**
* Emitted when a message is rejected/nacked and sent to dead letter queue
*/
'audit.dead_letter': {
/**
* The microservice that published the original event
*/
publisher_microservice: string;
/**
* The microservice that rejected the event
*/
rejector_microservice: string;
/**
* The original event that was rejected
*/
rejected_event: string;
/**
* Timestamp when the event was rejected (UNIX timestamp in milliseconds)
*/
rejected_at: number;
/**
* The queue name where the event was rejected from
*/
queue_name: string;
/**
* Reason for rejection (delay, fibonacci_strategy, etc.)
*/
rejection_reason: 'delay' | 'fibonacci_strategy';
/**
* Optional retry count
*/
retry_count?: number;
/**
* UUID v7 from message properties for cross-event tracking
*/
event_id: string;
};
/**
* Payment has been created and is pending
*/
'billing.payment_created': {
paymentId: string;
userId: string;
amount: number;
currency: string;
status: 'pending' | 'processing';
metadata: Record<string, string>;
occurredAt: string;
};
/**
* Payment completed successfully
*/
'billing.payment_succeeded': {
paymentId: string;
userId: string;
amount: number;
currency: string;
metadata: Record<string, string>;
occurredAt: string;
};
/**
* Payment failed
*/
'billing.payment_failed': {
paymentId: string;
userId: string;
amount: number;
currency: string;
failureReason: string | null;
metadata: Record<string, string>;
occurredAt: string;
};
/**
* Payment was refunded (fully or partially)
*/
'billing.payment_refunded': {
paymentId: string;
userId: string;
amount: number;
refundedAmount: number;
currency: string;
metadata: Record<string, string>;
occurredAt: string;
};
/**
* New subscription created
*/
'billing.subscription_created': {
subscriptionId: string;
userId: string;
planId: string;
planSlug: string;
status: 'pending' | 'active' | 'trialing';
periodStart: string;
periodEnd: string;
occurredAt: string;
};
/**
* Subscription was updated (plan change, status change, etc.)
*/
'billing.subscription_updated': {
subscriptionId: string;
userId: string;
planId: string;
planSlug: string;
status: 'active' | 'past_due' | 'unpaid' | 'paused' | 'trialing';
cancelAtPeriodEnd: boolean;
periodStart: string;
periodEnd: string;
occurredAt: string;
};
/**
* Subscription was renewed (new billing period started)
*/
'billing.subscription_renewed': {
subscriptionId: string;
userId: string;
planId: string;
planSlug: string;
periodStart: string;
periodEnd: string;
occurredAt: string;
};
/**
* Subscription was canceled (still active until period end)
*/
'billing.subscription_canceled': {
subscriptionId: string;
userId: string;
planId: string;
planSlug: string;
canceledAt: string;
occurredAt: string;
};
/**
* Subscription has expired (no longer active)
*/
'billing.subscription_expired': {
subscriptionId: string;
userId: string;
planId: string;
planSlug: string;
expiredAt: string;
occurredAt: string;
};
/**
* New event created
*/
'legend_events.new_event_created': {
eventId: number;
title: string;
description: string;
authorEmail: string;
rewardType?: string;
startDate: string;
endDate: string;
maxPlayers?: number;
ticketPriceUsd?: number;
isFreeTournament: boolean;
notificationConfig?: {
customEmails?: string[];
templateName: string;
};
};
/**
* Event has started
*/
'legend_events.event_started': {
eventId: number;
title: string;
startedAt: string;
};
/**
* Event has ended
*/
'legend_events.event_ended': {
eventId: number;
title: string;
endedAt: string;
totalParticipants: number;
};
/**
* Player registered for an event (paid or free)
*/
'legend_events.player_registered': {
eventId: number;
userId: string;
paymentId?: string;
amountPaid?: number;
isFree: boolean;
registeredAt: string;
};
/**
* Player joined the waitlist (event full)
*/
'legend_events.player_joined_waitlist': {
eventId: number;
userId: string;
position: number;
joinedAt: string;
};
/**
* Score submitted for an event
*/
'legend_events.score_submitted': {
eventId: number;
userId: string;
score: number;
totalScore: number;
matchId?: string;
submittedAt: string;
};
/**
* Events have finished, send emails to winners
*/
'legend_events.events_finished': {
completedEvents: Array<{
eventId: number;
title: string;
description: string;
authorEmail: string;
endsAt: string;
reward?: string;
rewardType?: string;
winners: Array<{
userId: string;
position: number;
score: number;
}>;
notificationConfig?: Record<string, unknown>;
}>;
};
/**
* Intermediate reward delivered during event
*/
'legend_events.intermediate_reward': {
userId: string;
eventId: number;
intermediateRewardType: string;
rewardConfig: Record<string, unknown>;
templateName: string;
templateData: Record<string, unknown>;
};
/**
* Participation reward delivered post-event
*/
'legend_events.participation_reward': {
userId: string;
eventId: number;
participationRewardType: string;
rewardConfig: Record<string, unknown>;
templateName: string;
templateData: Record<string, unknown>;
};
}
/**
* Represents the available events in the system.
*/
declare const microserviceEvent: {
readonly 'TEST.IMAGE': "test.image";
readonly 'TEST.MINT': "test.mint";
readonly 'AUDIT.PUBLISHED': "audit.published";
readonly 'AUDIT.RECEIVED': "audit.received";
readonly 'AUDIT.PROCESSED': "audit.processed";
readonly 'AUDIT.DEAD_LETTER': "audit.dead_letter";
readonly 'AUTH.DELETED_USER': "auth.deleted_user";
readonly 'AUTH.LOGOUT_USER': "auth.logout_user";
readonly 'AUTH.NEW_USER': "auth.new_user";
readonly 'AUTH.BLOCKED_USER': "auth.blocked_user";
readonly 'LEGEND_MISSIONS.NEW_MISSION_CREATED': "legend_missions.new_mission_created";
readonly 'LEGEND_MISSIONS.ONGOING_MISSION': "legend_missions.ongoing_mission";
readonly 'LEGEND_MISSIONS.MISSION_FINISHED': "legend_missions.mission_finished";
readonly 'LEGEND_MISSIONS.SEND_EMAIL_CRYPTO_MISSION_COMPLETED': "legend_missions.send_email_crypto_mission_completed";
readonly 'LEGEND_MISSIONS.SEND_EMAIL_CODE_EXCHANGE_MISSION_COMPLETED': "legend_missions.send_email_code_exchange_mission_completed";
readonly 'LEGEND_MISSIONS.SEND_EMAIL_NFT_MISSION_COMPLETED': "legend_missions.send_email_nft_mission_completed";
readonly 'LEGEND_RANKINGS.RANKINGS_FINISHED': "legend_rankings.rankings_finished";
readonly 'LEGEND_RANKINGS.NEW_RANKING_CREATED': "legend_rankings.new_ranking_created";
readonly 'LEGEND_RANKINGS.RANKING_SUBMITTED_FOR_REVIEW': "legend_rankings.ranking_submitted_for_review";
readonly 'LEGEND_RANKINGS.RANKING_APPROVED': "legend_rankings.ranking_approved";
readonly 'LEGEND_RANKINGS.RANKING_REJECTED': "legend_rankings.ranking_rejected";
readonly 'LEGEND_RANKINGS.RANKING_ACTIVATED': "legend_rankings.ranking_activated";
readonly 'LEGEND_RANKINGS.INTERMEDIATE_REWARD': "legend_rankings.intermediate_reward";
readonly 'LEGEND_RANKINGS.PARTICIPATION_REWARD': "legend_rankings.participation_reward";
readonly 'LEGEND_SHOWCASE.PRODUCT_VIRTUAL_DELETED': "legend_showcase.product_virtual_deleted";
readonly 'LEGEND_SHOWCASE.UPDATE_ALLOWED_MISSION_SUBSCRIPTION_IDS': "legend_showcase.update_allowed_mission_subscription_ids";
readonly 'LEGEND_SHOWCASE.UPDATE_ALLOWED_RANKING_SUBSCRIPTION_IDS': "legend_showcase.update_allowed_ranking_subscription_ids";
readonly 'SOCIAL.BLOCK_CHAT': "social.block_chat";
readonly 'SOCIAL.NEW_USER': "social.new_user";
readonly 'SOCIAL.UNBLOCK_CHAT': "social.unblock_chat";
readonly 'SOCIAL.UPDATED_USER': "social.updated_user";
readonly 'BILLING.PAYMENT_CREATED': "billing.payment_created";
readonly 'BILLING.PAYMENT_SUCCEEDED': "billing.payment_succeeded";
readonly 'BILLING.PAYMENT_FAILED': "billing.payment_failed";
readonly 'BILLING.PAYMENT_REFUNDED': "billing.payment_refunded";
readonly 'BILLING.SUBSCRIPTION_CREATED': "billing.subscription_created";
readonly 'BILLING.SUBSCRIPTION_UPDATED': "billing.subscription_updated";
readonly 'BILLING.SUBSCRIPTION_RENEWED': "billing.subscription_renewed";
readonly 'BILLING.SUBSCRIPTION_CANCELED': "billing.subscription_canceled";
readonly 'BILLING.SUBSCRIPTION_EXPIRED': "billing.subscription_expired";
readonly 'LEGEND_EVENTS.NEW_EVENT_CREATED': "legend_events.new_event_created";
readonly 'LEGEND_EVENTS.EVENT_STARTED': "legend_events.event_started";
readonly 'LEGEND_EVENTS.EVENT_ENDED': "legend_events.event_ended";
readonly 'LEGEND_EVENTS.PLAYER_REGISTERED': "legend_events.player_registered";
readonly 'LEGEND_EVENTS.PLAYER_JOINED_WAITLIST': "legend_events.player_joined_waitlist";
readonly 'LEGEND_EVENTS.SCORE_SUBMITTED': "legend_events.score_submitted";
readonly 'LEGEND_EVENTS.EVENTS_FINISHED': "legend_events.events_finished";
readonly 'LEGEND_EVENTS.INTERMEDIATE_REWARD': "legend_events.intermediate_reward";
readonly 'LEGEND_EVENTS.PARTICIPATION_REWARD': "legend_events.participation_reward";
};
/**
* Available microservices events in the system.
*/
type MicroserviceEvent = (typeof microserviceEvent)[keyof typeof microserviceEvent];
type Without<T, U> = {
[P in Exclude<keyof T, keyof U>]?: never;
};
type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
type Nack = XOR<{
delay: number;
maxRetries?: number;
}, {
maxOccurrence: number;
maxRetries?: number;
}>;
/**
* Abstract base class for handling the consumption of messages from RabbitMQ channels.
*
* This class provides common functionality for acknowledging (ACK) and negatively acknowledging (NACK) messages, with the ability to introduce delays and retry mechanisms. It's designed to be extended by specific consumer channel implementations.
*/
declare abstract class ConsumeChannel {
/**
* The AMQP Channel object used for communication with RabbitMQ.
*/
protected readonly channel: Channel;
/**
* The message received from RabbitMQ that this channel is currently processing.
*/
protected readonly msg: ConsumeMessage;
/**
* The name of the queue from which the message was consumed.
*/
protected readonly queueName: string;
/**
* Creates a new `ConsumeChannel` instance.
*
* @param channel - The AMQP Channel for interacting with RabbitMQ.
* @param msg - The consumed message.
* @param queueName - The name of the source queue.
*/
constructor(channel: Channel, msg: ConsumeMessage, queueName: string);
/**
* Acknowledges (ACKs) the message, indicating successful processing.
*
* @param payloadForNextStep - Optional payload to include for the next step in a multi-step process (e.g., saga).
*/
abstract ackMessage(payloadForNextStep?: Record<string, unknown>): void;
/**
* Negatively acknowledges (NACKs) the message with a specified delay and maximum retry count.
*
* This method is useful when you want to requeue the message for later processing, especially if the current attempt failed due to a temporary issue.
*
* @param delay - The delay (in milliseconds) before requeueing the message. Defaults to `NACKING_DELAY_MS`.
* @param maxRetries - The maximum number of times to requeue the message before giving up. Defaults to `undefined`, never giving up.
* @returns An object containing:
* - `count`: The current retry count.
* - `delay`: The actual delay applied to the nack.
*
* @see NACKING_DELAY_MS
*/
nackWithDelay(delay?: number, maxRetries?: number): {
count: number;
delay: number;
};
/**
* Negatively acknowledges (NACKs) the message using a Fibonacci backoff strategy.
*
* The delay before requeuing increases with each retry according to the Fibonacci sequence, helping to avoid overwhelming the system in case of repeated failures.
*
* @param maxOccurrence - The maximum number of times the Fibonacci delay is allowed to increase before being reset. Defaults to `MAX_OCCURRENCE`.
* @param maxRetries - The maximum number of times to requeue the message before giving up. Defaults to `undefined`, never giving up.
* @returns An object containing:
* - `count`: The current retry count.
* - `delay`: The calculated Fibonacci delay (in milliseconds) applied to the nack.
* - `occurrence`: The current occurrence count for the Fibonacci sequence.
* @see MAX_OCCURRENCE
*/
nackWithFibonacciStrategy(maxOccurrence?: number, maxRetries?: number): {
count: number;
delay: number;
occurrence: number;
};
/**
* Private helper function to handle the actual NACK logic.
*
* This method performs the NACK operation, manages retry counts and delays, and republishes the message for requeuing with appropriate headers and routing.
*
* @param nackOptions - An object specifying either:
* - `delay` and `maxRetries`: For linear backoff with a fixed delay and retry limit.
* - `maxOccurrence`: For Fibonacci backoff with a maximum occurrence count.
*/
private nack;
}
/**
* Represents a **_consume_** channel for handling saga events/commands.
* Extends the abstract ConsumeChannel class with automatic audit event emission.
*
*/
declare class EventsConsumeChannel extends ConsumeChannel {
/**
* The microservice name that is processing the event
*/
private readonly processorMicroservice;
/**
* The original event that was received
*/
private readonly processedEvent;
/**
* The unique event identifier (messageId) for tracking across audit events
*/
private readonly eventId;
/**
* The microservice that originally published the event
*/
private readonly publisherMicroservice;
/**
* Creates a new EventsConsumeChannel instance
*
* @param channel - The AMQP Channel
* @param msg - The consumed message
* @param queueName - The queue name
* @param processorMicroservice - The microservice name processing the event
* @param processedEvent - The event type being processed
* @param eventId - Unique event identifier (messageId) for audit tracking
* @param publisherMicroservice - The microservice that originally published the event
*/
constructor(channel: Channel, msg: ConsumeMessage, queueName: string, processorMicroservice: string, processedEvent: string, eventId: string, publisherMicroservice: string);
/**
* Acknowledges the consumed saga event/command.
* Automatically emits audit.processed event after successful ACK.
*/
ackMessage(): void;
/**
* Negatively acknowledges (NACKs) the message with a specified delay and maximum retry count.
*
* This method is useful when you want to requeue the message for later processing, especially if the current attempt failed due to a temporary issue.
*
* Additionally, this override automatically emits an `audit.dead_letter` event to track the rejection.
*
* @param delay - The delay (in milliseconds) before requeueing the message. Defaults to `NACKING_DELAY_MS`.
* @param maxRetries - The maximum number of times to requeue the message before giving up. Defaults to `undefined`, never giving up.
* @returns An object containing:
* - `count`: The current retry count.
* - `delay`: The actual delay applied to the nack.
*
* @see NACKING_DELAY_MS
*/
nackWithDelay(delay?: number, maxRetries?: number): {
count: number;
delay: number;
};
/**
* Negatively acknowledges (NACKs) the message using a Fibonacci backoff strategy.
*
* The delay before requeuing increases with each retry according to the Fibonacci sequence, helping to avoid overwhelming the system in case of repeated failures.
*
* Additionally, this override automatically emits an `audit.dead_letter` event to track the rejection.
*
* @param maxOccurrence - The maximum number of times the Fibonacci delay is allowed to increase before being reset. Defaults to `MAX_OCCURRENCE`.
* @param maxRetries - The maximum number of times to requeue the message before giving up. Defaults to `undefined`, never giving up.
* @returns An object containing:
* - `count`: The current retry count.
* - `delay`: The calculated Fibonacci delay (in milliseconds) applied to the nack.
* - `occurrence`: The current occurrence count for the Fibonacci sequence.
*
* @see MAX_OCCURRENCE
*/
nackWithFibonacciStrategy(maxOccurrence?: number, maxRetries?: number): {
count: number;
delay: number;
occurrence: number;
};
}
/**
* Represents handlers for events emitted by a microservice.
*/
interface EventsHandler<T extends MicroserviceEvent> {
payload: EventPayload[T & keyof EventPayload];
channel: EventsConsumeChannel;
}
/**
* Represents the events emitted by a microservice to Legendaryum.
*/
type MicroserviceConsumeEvents<T extends MicroserviceEvent> = {
[key in T]: EventsHandler<key>;
};
/**
* Represents the names of specific message queues in the RabbitMQ context.
*/
declare const queue: {
/**
* Audit queue names for separate audit event types
* @constant
*/
readonly AuditPublished: "audit_published_commands";
readonly AuditReceived: "audit_received_commands";
readonly AuditProcessed: "audit_processed_commands";
readonly AuditDeadLetter: "audit_dead_letter_commands";
/**
* Queue used for sending replies in response to saga events.
*/
readonly ReplyToSaga: "reply_to_saga";
/**
* Queue used for commencing a saga.
*/
readonly CommenceSaga: "commence_saga";
};
/**
* Represents the names of exchanges, which act as message routing hubs in the RabbitMQ context.
*/
declare const exchange: {
/**
* Audit exchange name for direct routing of audit events
*/
readonly Audit: "audit_exchange";
/**
* Exchange dedicated to requeueing messages that require further processing in a saga process
*/
readonly Requeue: "requeue_exchange";
/**
* Exchange for sending command messages to various consumers in a saga process
*/
readonly Commands: "commands_exchange";
/**
* Exchange used for replying to saga events from consumers.
*/
readonly ReplyToSaga: "reply_exchange";
/**
* Exchange used for starting a saga.
*/
readonly CommenceSaga: "commence_saga_exchange";
/**
* Exchange used for starting a saga.
*/
readonly Matching: "matching_exchange";
/**
* Exchange dedicated to requeueing messages that require further processing.
*/
readonly MatchingRequeue: "matching_requeue_exchange";
};
/**
* Represents the names of specific message queues in the RabbitMQ context.
*/
type Exchange = (typeof exchange)[keyof typeof exchange];
/**
* Properties defining a queue consumer within the RabbitMQ context.
*/
interface QueueConsumerProps {
/**
* The name of the queue that messages will be consumed from.
*/
queueName: string;
/**
* The associated exchange for the queue, used for routing messages.
*/
exchange: Exchange;
}
/**
* Different commands related to the "auth" microservice.
*/
declare const authCommands: {
/**
* Command to create a new user.
*/
readonly CreateUser: "create_user";
};
/**
* Available commands for the "auth" microservice.
*/
type AuthCommands = (typeof authCommands)[keyof typeof authCommands];
/**
* Different commands related to the "audit-eda" microservice.
*/
declare const auditEdaCommands: {};
/**
* Available commands for the "audit-eda" microservice.
*/
type AuditEdaCommands = (typeof auditEdaCommands)[keyof typeof auditEdaCommands];
/**
* Different commands related to the "blockchain" microservice.
*/
declare const blockchainCommands: {
/**
* Saga step to transfer crypto reward to the winner of a mission.
*/
readonly TransferMissionRewardToWinner: "crypto_reward:transfer_mission_reward_to_winner";
/**
* Saga step to make transfers to winners.
*/
readonly TransferRewardToWinners: "crypto_reward:transfer_reward_to_winners";
};
/**
* Available commands for the "blockchain" microservice.
*/
type BlockchainCommands = (typeof blockchainCommands)[keyof typeof blockchainCommands];
/**
* Different commands related to the "Image" microservice.
*/
declare const testImageCommands: {
/**
* Command to create an image.
*/
readonly CreateImage: "create_image";
/**
* Command to update a token for an image.
*/
readonly UpdateToken: "update_token";
};
/**
* Available commands for the "Image" microservice.
*/
type TestImageCommands = (typeof testImageCommands)[keyof typeof testImageCommands];
/**
* Different commands related to the "Mint" microservice.
*/
declare const testMintCommands: {
/**
* Command to mint an image.
*/
readonly MintImage: "mint_image";
};
/**
* Available commands for the "Mint" microservice.
*/
type TestMintCommands = (typeof testMintCommands)[keyof typeof testMintCommands];
/**
* Different commands related to the "social" microservice.
*/
declare const socialCommands: {
/**
* Command to create a new social user.
*/
readonly CreateSocialUser: "create_social_user";
/**
* Command to update the social user's image.
*/
readonly UpdateUserImage: "update_user:image";
};
/**
* Available commands for the "social" microservice.
*/
type SocialCommands = (typeof socialCommands)[keyof typeof socialCommands];
/**
* Different commands related to the "legend-showcase" microservice.
*/
declare const showcaseCommands: {};
/**
* Available commands for the "legend-showcase" microservice.
*/
type ShowcaseCommands = (typeof showcaseCommands)[keyof typeof showcaseCommands];
/**
* Different commands related to the "legend-storage" microservice.
*/
declare const storageCommands: {
/**
* Command to store a file from base64.
*/
readonly UploadFile: "upload_file";
};
/**
* Available commands for the "legend-storage" microservice.
*/
type StorageCommands = (typeof storageCommands)[keyof typeof storageCommands];
/**
* Different commands related to the "legend-missions" microservice.
*/
declare const missionsCommands: {};
/**
* Available commands for the "legend-missions" microservice.
*/
type MissionsCommands = (typeof missionsCommands)[keyof typeof missionsCommands];
/**
* Different commands related to the "send-email" microservice.
*/
declare const sendEmailCommands: {};
/**
* Available commands for the "send-email" microservice.
*/
type SendEmailCommands = (typeof sendEmailCommands)[keyof typeof sendEmailCommands];
/**
* Different commands related to the "rankings" microservice.
*/
declare const rankingsCommands: {};
/**
* Available commands for the "rankings" microservice.
*/
type RankingsCommands = (typeof rankingsCommands)[keyof typeof rankingsCommands];
/**
* Different commands related to the "transactional" microservice.
*/
declare const transactionalCommands: {};
/**
* Available commands for the "transactional" microservice.
*/
type TransactionalCommands = (typeof transactionalCommands)[keyof typeof transactionalCommands];
/**
* Different commands related to the "billing" microservice.
*/
declare const billingCommands: {};
/**
* Available commands for the "billing" microservice.
*/
type BillingCommands = (typeof billingCommands)[keyof typeof billingCommands];
/**
* Different commands related to the "legend-events" microservice.
*/
declare const legendEventsCommands: {};
/**
* Available commands for the "legend-events" microservice.
*/
type LegendEventsCommands = (typeof legendEventsCommands)[keyof typeof legendEventsCommands];
/**
* Different commands related to the "legend-game-analytics" microservice.
*/
declare const legendGameAnalyticsCommands: {};
/**
* Available commands for the "legend-game-analytics" microservice.
*/
type LegendGameAnalyticsCommands = (typeof legendGameAnalyticsCommands)[keyof typeof legendGameAnalyticsCommands];
/**
* A map that defines the relationship between microservices and their corresponding commands.
*/
interface CommandMap {
/**
* Test purpose
* Represents the mapping of "Image" microservice commands.
*/
[availableMicroservices.TestImage]: TestImageCommands;
/**
* Test purpose
* Represents the mapping of "Mint" microservice commands.
*/
[availableMicroservices.TestMint]: TestMintCommands;
/**
* Represents the mapping of "auth" microservice commands.
*/
[availableMicroservices.Auth]: AuthCommands;
/**
* Represents the mapping of "audit-eda" microservice commands.
*/
[availableMicroservices.AuditEda]: AuditEdaCommands;
/**
* Represents the mapping of "legend-billing" microservice commands.
*/
[availableMicroservices.Billing]: BillingCommands;
/**
* Represents the mapping of "blockchain" microservice commands.
*/
[availableMicroservices.Blockchain]: BlockchainCommands;
/**
* Represents the mapping of "legend-missions" microservice commands.
*/
[availableMicroservices.Missions]: MissionsCommands;
/**
* Represents the mapping of "rankings" microservice commands.
*/
[availableMicroservices.Rankings]: RankingsCommands;
/**
* Represents the mapping of "legend-send-email" microservice commands.
*/
[availableMicroservices.SendEmail]: SendEmailCommands;
/**
* Represents the mapping of "social" microservice commands.
*/
[availableMicroservices.Showcase]: ShowcaseCommands;
/**
* Represents the mapping of "social" microservice commands.
*/
[availableMicroservices.Social]: SocialCommands;
/**
* Represents the mapping of "legend-storage" microservice commands.
*/
[availableMicroservices.Storage]: StorageCommands;
/**
* Represents the mapping of "transactional" microservice commands.
*/
[availableMicroservices.Transactional]: TransactionalCommands;
/**
* Represents the mapping of "legend-events" microservice commands.
*/
[availableMicroservices.Events]: LegendEventsCommands;
/**
* Represents the mapping of "legend-game-analytics" microservice commands.
*/
[availableMicroservices.LegendGameAnalytics]: LegendGameAnalyticsCommands;
}
/**
* Represents a command specific to a microservice.
* T - The type of microservice for which the command is intended.
* @template T
*/
interface MicroserviceCommand<T extends AvailableMicroservices> {
/**
* The specific command associated with the microservice.
*/
command: CommandMap[T];
/**
* The microservice to which the command belongs.
*/
microservice: T;
}
/**
* Callback function for consuming a saga commence event.
*
* @param {ConsumeMessage | null} msg - The consumed message.
* @param {Channel} channel - The channel used for consuming messages.
* @param {Emitter<CommenceSagaEvents>} e - The emitter to emit events.
* @param {string} queueName - The name of the queue from which the message was consumed.
*/
declare const commenceSagaConsumeCallback: <U extends SagaTitle>(msg: ConsumeMessage | null, channel: Channel, e: Emitter<CommenceSagaEvents<U>>, queueName: string) => void;
/**
* Callback function for consuming and handling microservice events.
*
* This function is responsible for:
* 1. Parsing the incoming event message from the RabbitMQ queue.
* 2. Identifying the specific event type from message headers.
* 3. Emitting the event along with its payload to the provided emitter.
*
* If there are errors during message parsing or if invalid headers are found, the message is negatively acknowledged (NACKed) without requeueing.
*
* @template U - The specific type of microservice event being handled. Must be one of the types defined in the `MicroserviceEvent` enum.
*
* @param {ConsumeMessage | null} msg - The consumed message from RabbitMQ. Can be `null` if no message was available.
* @param {Channel} channel - The RabbitMQ channel used for consuming messages. This is used to acknowledge or reject messages.
* @param {Emitter<MicroserviceConsumeEvents<U>>} e - An event emitter that will broadcast the parsed event and its payload.
* @param {string} queueName - The name of the queue from which the message was consumed.
*/
declare const eventCallback: <U extends MicroserviceEvent>(msg: ConsumeMessage | null, channel: Channel, e: Emitter<MicroserviceConsumeEvents<U>>, queueName: string) => void;
/**
* Callback function for consuming saga events/commands.
*
* @typeparam T - The type of available microservices.
*
* @param {ConsumeMessage | null} msg - The consumed message.
* @param {Channel} channel - The channel used for consuming messages.
* @param {Emitter<SagaConsumeSagaEvents<T>>} e - The emitter to emit events.
* @param {string} queueName - The name of the queue from which the message was consumed.
*/
declare const sagaConsumeCallback: <T extends AvailableMicroservices>(msg: ConsumeMessage | null, channel: Channel, e: Emitter<SagaConsumeSagaEvents<T>>, queueName: string) => void;
interface MicroserviceHandler<T extends AvailableMicroservices> {
/**
* The ID of the saga associated with the event.
*/
sagaId: number;
/**
* The payload associated with the event.
*/
payload: Record<string, unknown>;
/**
* The channel used for consuming the event.
*/
channel: MicroserviceConsumeChannel<T>;
}
/**
* Represents the events emitted by the saga to the microservices.
*/
type MicroserviceConsumeSagaEvents<T extends AvailableMicroservices> = {
[key in CommandMap[T]]: MicroserviceHandler<T>;
};
/**
* Callback function for consuming microservice events/commands.
*
* @typeparam T - The type of available microservices.
*
* @param {ConsumeMessage | null} msg - The consumed message.
* @param {Channel} channel - The channel used for consuming messages.
* @param {Emitter<MicroserviceConsumeSagaEvents<T>>} e - The emitter to emit events.
* @param {string} queueName - The name of the queue from which the message was consumed.
*/
declare const sagaStepCallback: <T extends AvailableMicroservices>(msg: ConsumeMessage | null, channel: Channel, e: Emitter<MicroserviceConsumeSagaEvents<T>>, queueName: string) => void;
/**
* Class representing a consumer channel for processing sagas in a microservice environment.
*
*/
declare class SagaCommenceConsumeChannel extends ConsumeChannel {
/**
* Method to acknowledge the message.
*/
ackMessage(): void;
}
/**
* Represents a **_consume_** channel for a specific microservice.
* Extends the abstract ConsumeChannel class.
*
* @typeparam T - The type of available microservices.
*/
declare class MicroserviceConsumeChannel<T extends AvailableMicroservices> extends ConsumeChannel {
/**
* The saga step associated with the consumed message.
*/
protected readonly step: SagaStep<T>;
/**
* Constructs a new instance of the ConsumeChannel class.
*
* @param {Channel} channel - The channel to interact with the message broker.
* @param {ConsumeMessage} msg - The consumed message to be processed.
* @param {string} queueName - The name of the queue from which the message was consumed.
* @param {SagaStep} step - The saga step associated with the consumed message.
*/
constructor(channel: Channel, msg: ConsumeMessage, queueName: string, step: SagaStep<T>);
ackMessage(payloadForNextStep?: Record<string, unknown>): void;
}
/**
* Represents a **_consume_** channel for handling saga events/commands.
* Extends the MicroserviceConsumeChannel class.
*
*/
declare class SagaConsumeChannel<T extends AvailableMicroservices> extends MicroserviceConsumeChannel<T> {
/**
* Acknowledges the consumed saga event/command.
*/
ackMessage(): void;
}
/**
* Consume messages from a specified queue and process them using the provided callback function.
*
* @param {Emitter<E>} e - An emitter to listen to events related to the consumed messages with The event type(s) associated with the consumed messages.
* @param {string} queueName - The name of the queue to consume messages from.
* @param {(msg: ConsumeMessage | null, channel: Channel, e: Emitter<E>, queueName: string) => void} cb - The callback function to process consumed messages.
* @throws {Error} If there is an issue with establishing the consume channel or consuming messages.
*
* @typeParam E - The event type(s) associated with the consumed messages.
*/
declare const consume: <E extends Record<EventType, unknown>>(e: Emitter<E>, queueName: string, cb: (msg: ConsumeMessage | null, channel: Channel, e: Emitter<E>, queueName: string) => void) => Promise<void>;
/**
* Create consumers for specified queues, bind them to exchanges, and set up requeue mechanism.
*
* @param {QueueConsumerProps[]} consumers - An array of queue consumer properties to set up consumers for.
* @throws {Error} If there are issues with establishing the consume channel, creating queues, or binding exchanges.
*
* @example
* const consumers = [
* {
* queueName: 'my_queue',
* exchange: Exchange.Commands
* },
* // Add more queue consumers here...
* ];
* await createConsumers(consumers);
*
* // Once consumers are set up, they will start processing messages.
* @see startGlobalSagaStepListener
* @see connectToSagaCommandEmitter
*/
declare const createConsumers: (consumers: QueueConsumerProps[]) => Promise<void>;
/**
* Configures RabbitMQ exchanges, queues, and bindings for consuming specific microservice events with requeue capabilities.
*
* This function is crucial for setting up the infrastructure to receive events from the RabbitMQ message broker. It establishes a flexible system for:
*
* 1. Routing events to specific microservices based on headers.
* 2. Requeueing failed events for later processing.
*
* The function performs the following steps:
*
* **General Setup:**
* - Obtains a consume channel from RabbitMQ.
* - Asserts the `Matching` exchange where events will be initially published.
* - Asserts the `MatchingRequeue` exchange for handling requeued messages.
* - Asserts the main queue where events will be consumed (`queueName`). Is a specific queue for a microservice.
* - Asserts the requeue queue for storing messages that need to be reprocessed.
* - Sets up the requeue queue to send messages back to the `Matching` exchange if they remain unprocessed (dead-letter exchange mechanism).
*
* **Per-Event Configuration:**
* - Iterates through all possible `MicroserviceEvent` types.
* - Asserts event-specific exchanges (e.g., 'ORDER_CREATED', 'PAYMENT_FAILED').
* - Binds these event exchanges to the `Matching` exchange, allowing events to be ro