@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
TypeScript
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;
}