UNPKG

legend-transactional

Version:

A simple transactional, event-driven communication framework for microservices using RabbitMQ

1,482 lines (1,440 loc) 64 kB
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 "auth" microservice. */ readonly Auth: "auth"; /** * Represents the "blockchain" microservice. */ readonly Blockchain: "blockchain"; /** * Represents the "coins" microservice. */ readonly Coins: "coins"; /** * Represents the "legend-missions" microservice. */ readonly Missions: "legend-missions"; /** * Represents the "rankings" microservice. */ readonly Rankings: "rankings"; /** * Represents the "rapid-messaging" microservice. */ readonly RapidMessaging: "rapid-messaging"; /** * Represents the "room-creator" microservice. */ readonly RoomCreator: "room-creator"; /** * Represents the "room-inventory" microservice. */ readonly RoomInventory: "room-inventory"; /** * Represents the "room-snapshot" microservice. */ readonly RoomSnapshot: "room-snapshot"; /** * 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 "social-media-rooms" microservice. */ readonly SocialMediaRooms: "social-media-rooms"; /** * Represents the "legend-storage" microservice. */ readonly Storage: "legend-storage"; }; /** * Type of available microservices in the system. */ type AvailableMicroservices = (typeof availableMicroservices)[keyof typeof availableMicroservices]; /** * Types of rooms */ declare const RoomTypes: { readonly ISLAND: "island"; readonly HOUSE: "house"; readonly HALL_OF_FAME: "hallOfFame"; }; /** * Type of RoomTypes */ type RoomType = (typeof RoomTypes)[keyof typeof RoomTypes]; /** * Room in Room Creator */ interface Room { Id: string; CreateAt: string; UpdateAt: string; type: RoomType; name: string; ownerId: string; ownerEmail: string; maxPlayers: number; maxLayers: number; templateId: string; haveEditor: boolean; } /** * Types of Payment Email Types */ declare const PaymentEmailTypes: { readonly PURCHASE: "purchase"; readonly SUBSCRIPTION: "subscription"; readonly NEW_SUBSCRIPTION: "new_subscription"; }; /** * Type of Payment Email Types */ type PaymentEmailType = (typeof PaymentEmailTypes)[keyof typeof PaymentEmailTypes]; /** * Represents the winners of a ranking with their respective rewards */ interface RankingWinners { userId: string; reward: number; } /** * 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; } /** * 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 update a user's subscription. */ 'coins.update_subscription': { userId: string; paidPriceId: string; }; /** * Websocket event to notify the client about a coins related event. */ 'coins.notify_client': { room: `coins-${string}`; message: Record<string, unknown>; }; /** * Event to send email to users. */ 'coins.send_email': { userId: string; email: string; emailType: PaymentEmailType; coins: number; }; /** * Event to give the user coins for completing a mission */ 'legend_missions.completed_mission_reward': { userId: string; coins: 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 notify when a ranking is created */ 'legend_rankings.new_ranking_created': { title: string; description: string; authorEmail: string; rewardType: string; endsAt: string; nftBlockchainNetwork?: string; nftContractAddress?: string; walletCryptoAsset?: string; notificationConfig?: { customEmails?: string[]; templateName: 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 notify the creation of a room. */ 'room_creator.created_room': { room: Room; }; /** * Event emitted when a room is updated. */ 'room_creator.updated_room': { room: Room; }; /** * Event emitted when the image of a virtual product's building is updated */ 'room_inventory.update_vp_building_image': { images: string[]; roomType: string; userId: string; }; /** * Event emitted when a user changes buildings within the island */ 'room_snapshot.building_change_in_island': { userId: string; building: string; }; /** * Event to notify the first saved snapshot of a room. */ 'room_snapshot.first_snapshot': { slug: 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; }; /** * Event to delete assets in batch */ 'social_media_rooms.delete_in_batch': { bucketName: string; filePaths: string[]; }; } /** * Represents the available events in the system. */ declare const microserviceEvent: { readonly 'TEST.IMAGE': "test.image"; readonly 'TEST.MINT': "test.mint"; 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 'COINS.NOTIFY_CLIENT': "coins.notify_client"; readonly 'COINS.SEND_EMAIL': "coins.send_email"; readonly 'COINS.UPDATE_SUBSCRIPTION': "coins.update_subscription"; readonly 'LEGEND_MISSIONS.COMPLETED_MISSION_REWARD': "legend_missions.completed_mission_reward"; 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.INTERMEDIATE_REWARD': "legend_rankings.intermediate_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 'ROOM_CREATOR.CREATED_ROOM': "room_creator.created_room"; readonly 'ROOM_CREATOR.UPDATED_ROOM': "room_creator.updated_room"; readonly 'ROOM_INVENTORY.UPDATE_VP_BUILDING_IMAGE': "room_inventory.update_vp_building_image"; readonly 'ROOM_SNAPSHOT.BUILDING_CHANGE_IN_ISLAND': "room_snapshot.building_change_in_island"; readonly 'ROOM_SNAPSHOT.FIRST_SNAPSHOT': "room_snapshot.first_snapshot"; 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 'SOCIAL_MEDIA_ROOMS.DELETE_IN_BATCH': "social_media_rooms.delete_in_batch"; }; /** * 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. * */ declare class EventsConsumeChannel extends ConsumeChannel { /** * Acknowledges the consumed saga event/command. */ ackMessage(): void; } /** * 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: { /** * 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: { /** * 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 "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 "coins" microservice. */ declare const coinsCommands: { /** * Saga step to deduct coins when a user purchase a resource */ readonly DeductCoins: "resource_purchased:deduct_coins"; /** * Saga step to reward coins to users based on their rankings */ readonly RankingsRewardCoins: "rankings_users_reward:reward_coins"; }; /** * Available commands for the "coins" microservice. */ type CoinsCommands = (typeof coinsCommands)[keyof typeof coinsCommands]; /** * Different commands related to the "room-creator" microservice. */ declare const roomCreatorCommands: { /** * Command to update the island room template. */ readonly update_island_room_template: "update_island_room_template"; }; /** * Available commands for the "room-creator" microservice. */ type RoomCreatorCommands = (typeof roomCreatorCommands)[keyof typeof roomCreatorCommands]; /** * Different commands related to the "legend-showcase" microservice. */ declare const showcaseCommands: { /** * Command to update the product virtual and randomize the pv-image related. */ readonly randomize_island_pv_image: "randomize_island_pv_image"; }; /** * 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 "room-snapshot" microservice. */ declare const roomSnapshotCommands: {}; /** * Available commands for the "room-snapshot" microservice. */ type RoomSnapshotCommands = (typeof roomSnapshotCommands)[keyof typeof roomSnapshotCommands]; /** * Different commands related to the "room-inventory" microservice. */ declare const roomInventoryCommands: { /** * Command to save a purchased resource on user inventory */ readonly SavePurchasedResource: "resource_purchased:save_purchased_resource"; }; /** * Available commands for the "room-inventory" microservice. */ type RoomInventoryCommands = (typeof roomInventoryCommands)[keyof typeof roomInventoryCommands]; /** * Different commands related to the "rapid-messaging" microservice. */ declare const rapidMessagingCommands: {}; /** * Available commands for the "rapid-messaging" microservice. */ type RapidMessagingCommands = (typeof rapidMessagingCommands)[keyof typeof rapidMessagingCommands]; /** * 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 "social-media-rooms" microservice. */ declare const socialMediaRoomsCommands: {}; /** * Available commands for the "social-media-rooms" microservice. */ type SocialMediaRoomsCommands = (typeof socialMediaRoomsCommands)[keyof typeof socialMediaRoomsCommands]; /** * 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]; /** * 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 "blockchain" microservice commands. */ [availableMicroservices.Blockchain]: BlockchainCommands; /** * Represents the mapping of "coins" microservice commands. */ [availableMicroservices.Coins]: CoinsCommands; /** * 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 "rapid-messaging" microservice commands. */ [availableMicroservices.RapidMessaging]: RapidMessagingCommands; /** * Represents the mapping of "room-creator" microservice commands. */ [availableMicroservices.RoomCreator]: RoomCreatorCommands; /** * Represents the mapping of "room-inventory" microservice commands. */ [availableMicroservices.RoomInventory]: RoomInventoryCommands; /** * Represents the mapping of "room-snapshot" microservice commands. */ [availableMicroservices.RoomSnapshot]: RoomSnapshotCommands; /** * 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 "social-media-rooms" microservice commands. */ [availableMicroservices.SocialMediaRooms]: SocialMediaRoomsCommands; /** * Represents the mapping of "legend-storage" microservice commands. */ [availableMicroservices.Storage]: StorageCommands; } /** * 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 routed based on headers. * - Asserts requeue exchanges for each event type (e.g., 'ORDER_CREATED_requeue'). * - Binds requeue exchanges to the `MatchingRequeue` exchange, creating pathways for requeueing specific events. * * **Microservice-Specific Bindings:** * - If the `events` array includes a specific event type: * - The main queue (`queueName`) is bound to the event exchange, allowing the microservice to receive that event type. * - The requeue queue is bound to the event's requeue exchange, setting up a mechanism for requeueing failed events back to the microservice. * - An additional exchange is created for the microservice and the event, ensuring the requeued message is sent only to the microservice that originally failed to process it. * * - If the `events` array doesn't include the event type: * - Any existing bindings for that event type are removed. * - The associated exchanges are deleted if they are no longer in use. * * **Concurrency Control:** * - Sets the prefetch count to `1`, ensuring the microservice processes only one message at a time to maintain order and avoid race conditions. * * @param queueName - The name of the queue where the microservice will consume events. Is a specific queue for a microservice. * @param events - An array of `MicroserviceEvent` types that the microservice is interested in consuming. */ declare const createHeaderConsumers: (queueName: string, events: MicroserviceEvent[]) => Promise<void>; declare const sagaTitle: { /** * Saga used in the flow to purchase resources and deduct coins from the user. */ readonly PurchaseResourceFlow: "purchase_resource_flow"; /** * Saga used in to reward users based on their rankings. */ readonly RankingsUsersReward: "rankings_users_reward"; /** * Saga used to initiate a crypto transfer for a mission winner. */ readonly TransferCryptoRewardToMissionWinner: "transfer_crypto_reward_to_mission_winner"; /** * Saga used to initiate a crypto transfer for ranking winners. */ readonly TransferCryptoRewardToRankingWinners: "transfer_crypto_reward_to_ranking_winners"; }; /** * Available saga titles. */ type SagaTitle = (typeof sagaTitle)[keyof typeof sagaTitle]; interface SagaCommencePayload { ['purchase_resource_flow']: { userId: string; resourceId: string; price: number; quantity: number; }; ['rankings_users_reward']: { rewards: { userId: string; coins: number; }[]; }; ['transfer_crypto_reward_to_mission_winner']: { walletAddress: string; userId: string; reward: string; }; ['transfer_crypto_reward_to_ranking_winners']: { completedCryptoRankings: { walletAddress: string; winners: { userId: string; reward: string; }[]; }[]; }; } /** * Represents the payload of a saga commence event. */ interface CommenceSaga<U extends SagaTitle> { title: U; /** * The payload associated with the event. */ payload: SagaCommencePayload[U]; } interface CommenceSagaHandler<U extends SagaTitle> { /** * The saga associated with the event. */ saga: CommenceSaga<U>; /** * The channel used for consuming the event. */ channel: SagaCommenceConsumeChannel; } /** * Represents the saga title emitted to commence a saga. */ type CommenceSagaEvents<U extends SagaTitle> = { [key in U]: CommenceSagaHandler<key>; }; /** * Represents the different statuses that a saga step can have. */ declare const status: { /** * The step is pending and hasn't been processed yet. */ readonly Pending: "pending"; /** * The step has been successfully executed. */ readonly Success: "success"; /** * The step execution has failed. */ readonly Failure: "failure"; /** * The step has been sent but not yet executed. */ readonly Sent: "sent"; }; /** * Type of available statuses for a saga step. */ type Status = (typeof status)[keyof typeof status]; /** * Represents the default properties of a saga step. */ interface SagaStepDefaults { /** * The status of the saga step. */ status: Status; /** * The payload associated with the saga step. */ payload: Record<string, unknown>; /** * The previous payload of the saga step. */ previousPayload: Record<string, unknown>; /** * Indicates if the step is the current active step. */ isCurrentStep: boolean; } /** * Represents a saga step for a specific microservice. */ interface SagaStep<T extends AvailableMicroservices> extends SagaStepDefaults, MicroserviceCommand<T> { /** * The microservice associated with the step. */ microservice: T; /** * The command type based on the microservice, ensuring correctness. */ command: CommandMap[T]; /** * The status of the saga step. */ status: Status; /** * The ID of the saga. */ sagaId: number; /** * The payload associated with the saga step. */ payload: Record<string, unknown>; /** * The previous payload of the saga step. */ previousPayload: Record<string, unknown>; /** * Indicates if the step is the current active step. */ isCurrentStep: boolean; } interface SagaHandler<T extends AvailableMicroservices> { /** * The saga step associated with the event. */ step: SagaStep<T>; /** * The channel used for consuming the event. */ channel: SagaConsumeChannel<T>; } /** * Represents the saga step emitted from a specific microservice to the saga. */ type SagaConsumeSagaEvents<T extends AvailableMicroservices> = { [key in CommandMap[T]]: SagaHandler<T>; }; /** * Get the **_send_** channel for sending messages to a queue. * * @returns {Promise<Channel>} A promise that resolves to the **_send_** channel. * @throws {Error} If there is an issue with creating the **_send_** channel or getting the RabbitMQ connection. */ declare const getSendChannel: () => Promise<Channel>; /** * Close the **_send_** channel if it is open. * * @returns {Promise<void>} A promise that resolves when the **_send_** channel is successfully closed. * @throws {Error} If there is an issue with closing the **_send_** channel. */ declare const closeSendChannel: () => Promise<void>; /** * Sends a message payload to a specified queue. * * @template T - The type of the message payload. This allows for type-safe handling of different payloads across queues. * @param {string} queueName - The name of the queue to send the message to. * @param {T} payload - The message payload to send. * @async * @returns {Promise<void>} A promise that resolves when the message has been successfully sent. */ declare const sendToQueue: <T extends Record<string, any>>(queueName: string, payload: T) => Promise<void>; /** * Commences a saga by sending a message payload to the `CommenceSaga` queue. * * Sagas are long-running processes composed of multiple steps, typically coordinated across services. This function initiates a new saga instance. * * @template U - The specific type of the saga being commenced. This must be one of the predefined types in the `SagaTitle` enum. * @param {U} sagaTitle - The title of the saga to commence. This acts as an identifier for the specific saga workflow. * @param {SagaCommencePayload[U]} payload - The payload data required to start the saga. The structure of this payload is specific to the saga type `U`. * @async * @returns {Promise<void>} A promise that resolves when the saga commencement message has been successfully sent to the queue. * * @see SagaTitle */ declare const commenceSaga: <U extends SagaTitle>(sagaTitle: U, payload: SagaCommencePayload[U]) => Promise<void>; /** * Publishes a microservice event to all subscribed microservices. * * @template T - The type of the microservice event being published. Must extend `MicroserviceEvent`. * @param msg - The event payload data. The structure should match the expected payload for the event type (`T`). * @param event - The event identifier (e.g., 'ORDER_CREATED', 'PAYMENT_FAILED'). This must be one of the predefined event types in your `MicroserviceEvent` enum. * @async * @returns A Promise that resolves when the event has been successfully published. * * @example * ```typescript * import { publishEvent } from './publishEvent'; * import { MicroserviceEvent } from '../@types'; * * const orderData = { id: 123, customer: 'John Doe', total: 99.99 }; * await publishEvent(orderData, MicroserviceEvent.ORDER_CREATED); * ``` * * @see MicroserviceEvent */ declare const publishEvent: <T extends MicroserviceEvent>(msg: EventPayload[T & keyof EventPayload], event: T) => Promise<void>; /** * Get the _**consume**_ channel for consuming messages from RabbitMQ. * * @returns {Promise<Channel>} A promise that resolves to the _**consume**_ channel. * @throws {Error} If there is an issue with creating the _**consume**_ channel or the RabbitMQ connection. */ declare const getConsumeChannel: () => Promise<Channel>; /** * Close the consume channel used for consuming messages from RabbitMQ. * * @returns {Promise<void>} A promise that resolves when the consume channel is successfully closed. * @throws {Error} If there is an issue with closing the consume channel. */ declare const closeConsumeChannel: () => Promise<void>; /** * Save the RabbitMQ URI for establishing a connection. * * @param {string} uri - The RabbitMQ URI to save. */ declare const saveUri: (uri: string) => void; /** * Get the RabbitMQ connection or establish a new connection if not already connected. * * @returns {Promise<Connection>} A promise that resolves to the RabbitMQ connection. */ declare const getRabbitMQConn: () => Promise<ChannelModel>; /** * Close the RabbitMQ connection if it is open. * * @returns {Promise<void>} A promise that resolves when the connection is successfully closed. */ declare const closeRabbitMQConn: () => Promise<void>; /** * Save the queue name for health check purposes. * @param queue */ declare const saveQueueForHealthCheck: (queue: string) => void; /** * Checks the health of a RabbitMQ connection by creating a test channel and checking an already created queue. * @returns {Promise<boolean>} A promise that resolves to `true` if the connection is healthy and the queue exists, or `false` otherwise. */ declare const isConnectionHealthy: () => Promise<boolean>; /** * Start a global saga listener to handle incoming saga events/commands from all microservices. * * @param {string} url - The RabbitMQ URL to establish a connection. * * @returns {Promise<Emitter>} Emitter<ConsumerSagaEvents<T>> - A promise that resolves to an emitter * for handling the saga events emitted by the specified microservice. * @throws {Error} If the RabbitMQ URI is not initialized or there is an issue with the connection. * * @template T * @example * await prepare('amqp://localhost'); * const sagaEmitter = await startGlobalSagaListener<AvailableMicroservices>(); * // Also valid and equivalent to the above * const g = await startGlobalSagaListener(); * * // Listen to a single microservice saga event * g.on(MintCommands.MintImage, async ({ channel, step }) => { * // ... do something, ack or nack the message * }); * * // Listen to all microservice saga events, take notice that the event already listen above it needs to be ignored here. * g.on('*', async (command, { step, channel }) => { * if (command === MintCommands.MintImage) { * // Ignore the event already listened above * return; * } *