UNPKG

@message-queue-toolkit/core

Version:

Useful utilities, interfaces and base classes for message queue handling. Supports AMQP and SQS with a common abstraction on top currently

128 lines (127 loc) 7.35 kB
import type { Either } from '@lokalise/node-core'; import type { CommonEventDefinition } from '@message-queue-toolkit/schemas'; import type { ZodSchema } from 'zod/v4'; import type { DoNotProcessMessageError } from '../errors/DoNotProcessError.ts'; import type { RetryMessageLaterError } from '../errors/RetryMessageLaterError.ts'; import { type MessageTypeResolverConfig } from './MessageTypeResolver.ts'; export type PreHandlingOutputs<PrehandlerOutput = undefined, BarrierOutput = undefined> = { preHandlerOutput: PrehandlerOutput; barrierOutput: BarrierOutput; }; export type LogFormatter<MessagePayloadSchema> = (message: MessagePayloadSchema) => unknown; export type BarrierResult<BarrierOutput> = BarrierResultPositive<BarrierOutput> | BarrierResultNegative; export type BarrierResultPositive<BarrierOutput> = { isPassing: true; output: BarrierOutput; }; export type BarrierResultNegative = { isPassing: false; output?: never; }; export type PrehandlerResult = Either<DoNotProcessMessageError | RetryMessageLaterError, 'success'>; export type BarrierCallback<MessagePayloadSchema extends object, ExecutionContext, PrehandlerOutput, BarrierOutput> = (message: MessagePayloadSchema, context: ExecutionContext, preHandlerOutput: PrehandlerOutput) => Promise<BarrierResult<BarrierOutput>>; export type Prehandler<MessagePayloadSchema extends object, ExecutionContext, PrehandlerOutput> = (message: MessagePayloadSchema, context: ExecutionContext, preHandlerOutput: Partial<PrehandlerOutput>, next: (result: PrehandlerResult) => void) => void; export type HandlerConfigOptions<MessagePayloadSchema extends object, ExecutionContext, PrehandlerOutput, BarrierOutput> = { /** * Explicit message type for this handler. * Required when using a custom resolver function that cannot extract types from schemas. * Optional when using messageTypePath or literal resolver modes. */ messageType?: string; messageLogFormatter?: LogFormatter<MessagePayloadSchema>; preHandlerBarrier?: BarrierCallback<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>; preHandlers?: Prehandler<MessagePayloadSchema, ExecutionContext, PrehandlerOutput>[]; }; export declare class MessageHandlerConfig<const MessagePayloadSchema extends object, const ExecutionContext, const PrehandlerOutput = undefined, const BarrierOutput = unknown> { readonly schema: ZodSchema<MessagePayloadSchema>; readonly definition?: CommonEventDefinition; /** * Explicit message type for this handler, if provided. * Used for routing when type cannot be extracted from schema. */ readonly messageType?: string; readonly handler: Handler<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>; readonly messageLogFormatter?: LogFormatter<MessagePayloadSchema>; readonly preHandlerBarrier?: BarrierCallback<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>; readonly preHandlers: Prehandler<MessagePayloadSchema, ExecutionContext, PrehandlerOutput>[]; constructor(schema: ZodSchema<MessagePayloadSchema>, handler: Handler<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>, options?: HandlerConfigOptions<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>, eventDefinition?: CommonEventDefinition); } export declare class MessageHandlerConfigBuilder<MessagePayloadSchemas extends object, ExecutionContext, PrehandlerOutput = undefined> { private readonly configs; constructor(); /** * Add a handler configuration for a specific message type. * The schema is used for both routing (to match the message type) and validation (for the handler). * * The message type field (e.g., 'type' or 'detail-type') must be at the root level of the message * and must be a literal value in the schema for routing to work. * * Example: * ```typescript * const USER_CREATED_SCHEMA = z.object({ * type: z.literal('user.created'), * userId: z.string(), * email: z.string() * }) * * builder.addConfig(USER_CREATED_SCHEMA, async (message) => { * // message has type 'user.created', userId, and email * }) * ``` * * EventBridge example: * ```typescript * const USER_PRESENCE_SCHEMA = z.object({ * 'detail-type': z.literal('v2.users.{id}.presence'), * time: z.string(), * detail: z.object({ * userId: z.string(), * presenceStatus: z.string() * }) * }) * * builder.addConfig(USER_PRESENCE_SCHEMA, async (message) => { * // message is the full EventBridge envelope * const detail = message.detail // Access nested payload directly * }) * ``` */ addConfig<MessagePayloadSchema extends MessagePayloadSchemas, const BarrierOutput>(schema: ZodSchema<MessagePayloadSchema> | CommonEventDefinition, handler: Handler<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>, options?: HandlerConfigOptions<MessagePayloadSchema, ExecutionContext, PrehandlerOutput, BarrierOutput>): this; build(): MessageHandlerConfig<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput, any>[]; } export type Handler<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput = undefined, BarrierOutput = undefined> = (message: MessagePayloadSchemas, context: ExecutionContext, preHandlingOutputs: PreHandlingOutputs<PrehandlerOutput, BarrierOutput>, definition?: CommonEventDefinition) => Promise<Either<'retryLater', 'success'>>; export type HandlerContainerOptions<MessagePayloadSchemas extends object, ExecutionContext, PrehandlerOutput = undefined> = { messageHandlers: MessageHandlerConfig<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput>[]; /** * Configuration for resolving message types. */ messageTypeResolver?: MessageTypeResolverConfig; }; export declare class HandlerContainer<MessagePayloadSchemas extends object, ExecutionContext, PrehandlerOutput = undefined> { private readonly messageHandlers; private readonly messageTypeResolver?; constructor(options: HandlerContainerOptions<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput>); /** * Resolves a handler for the given message type. */ resolveHandler<PrehandlerOutput = undefined, BarrierOutput = undefined>(messageType: string): MessageHandlerConfig<MessagePayloadSchemas, ExecutionContext, PrehandlerOutput, BarrierOutput>; /** * Resolves message type from message data and optional attributes using the configured resolver. * * @param messageData - The parsed message data * @param messageAttributes - Optional message-level attributes (e.g., PubSub attributes) * @returns The resolved message type * @throws Error if message type cannot be resolved */ resolveMessageType(messageData: unknown, messageAttributes?: Record<string, unknown>): string; /** * Gets the field path used for extracting message type from schemas during registration. * Returns undefined for literal or custom resolver modes. */ private getMessageTypePathForSchema; /** * Gets the literal message type if configured. */ private getLiteralMessageType; private resolveHandlerMap; }