import { StandardSchemaV1 } from "@standard-schema/spec"; //#region src/types.d.ts /** * Any schema that conforms to Standard Schema v1. * * This library supports any validation library that implements the Standard Schema v1 specification, * including Zod, Valibot, and ArkType. This allows you to use your preferred validation library * while maintaining type safety. * * @see https://github.com/standard-schema/standard-schema */ type AnySchema = StandardSchemaV1; /** * TTL-Backoff retry options for exponential backoff with configurable delays. * * Uses TTL + wait queue pattern. Messages are published to a wait queue with * per-message TTL, then dead-lettered back to the main queue after the TTL expires. * * **Benefits:** Configurable delays with exponential backoff and jitter. * **Limitation:** More complex, potential head-of-queue blocking with mixed TTLs. */ type TtlBackoffRetryOptions = { /** * TTL-Backoff mode uses wait queues with per-message TTL for exponential backoff. */ mode: "ttl-backoff"; /** * Maximum retry attempts before sending to DLQ. * @minimum 1 - Must be a positive integer (1 or greater) * @default 3 */ maxRetries?: number; /** * Initial delay in ms before first retry. * @default 1000 */ initialDelayMs?: number; /** * Maximum delay in ms between retries. * @default 30000 */ maxDelayMs?: number; /** * Exponential backoff multiplier. * @default 2 */ backoffMultiplier?: number; /** * Add jitter to prevent thundering herd. * @default true */ jitter?: boolean; /** * Name of the wait queue. * @default '{queueName}-wait' */ waitQueueName?: string; /** * Name of the wait exchange. * @default 'wait-exchange' */ waitExchangeName?: string; /** * Name of the retry exchange. * @default 'retry-exchange' */ retryExchangeName?: string; }; /** * Immediate-Requeue retry options. * * Failed messages are requeued immediately. * For quorum queues, messages are requeued with `nack(requeue=true)`, and the worker tracks delivery count via the native RabbitMQ `x-delivery-count` header. * For classic queues, messages are re-published on the same queue, and the worker tracks delivery count via a custom `x-retry-count` header. * When the count exceeds `maxRetries`, the message is automatically dead-lettered (if DLX is configured) or dropped. * * **Benefits:** Simpler architecture, no wait queues needed, no head-of-queue blocking. * **Limitation:** Immediate retries only (no exponential backoff). * * @see https://www.rabbitmq.com/docs/quorum-queues#poison-message-handling */ type ImmediateRequeueRetryOptions = { /** * Immediate-Requeue mode. */ mode: "immediate-requeue"; /** * Maximum retry attempts before sending to DLQ. * @minimum 1 - Must be a positive integer (1 or greater) * @default 3 */ maxRetries?: number; }; /** * No retry mode. Failed messages are not retried and are sent * directly to DLQ (if configured) or rejected. */ type NoneRetryOptions = { /** * None mode disables retry attempts entirely. */ mode: "none"; }; /** * Retry configuration options. * * This is a discriminated union based on the `mode` field: * - `none` (default): No retry attempts are made; failed messages are handled by DLQ/reject * - `immediate-requeue`: Requeues failed messages immediately * - `ttl-backoff`: Uses wait queues with exponential backoff */ type RetryOptions = NoneRetryOptions | ImmediateRequeueRetryOptions | TtlBackoffRetryOptions; /** * Resolved TTL-Backoff retry options with all defaults applied. * * This type is used internally in queue definitions after `defineQueue` has applied * default values. All fields are required. * * @internal */ type ResolvedTtlBackoffRetryOptions = { mode: "ttl-backoff"; maxRetries: number; initialDelayMs: number; maxDelayMs: number; backoffMultiplier: number; jitter: boolean; waitQueueName: string; waitExchangeName: string; retryExchangeName: string; }; /** * Resolved Immediate-Requeue retry options with all defaults applied. * * This type is used internally in queue definitions after `defineQueue` has applied * default values. All fields are required. * * @internal */ type ResolvedImmediateRequeueRetryOptions = { mode: "immediate-requeue"; maxRetries: number; }; /** * Resolved retry configuration stored in queue definitions. * * This is a discriminated union based on the `mode` field: * - `none`: No retry attempts are made; failed messages are handled by DLQ/reject * - `immediate-requeue`: Has all immediate-requeue retry options with default applied * - `ttl-backoff`: Has all TTL-backoff retry options with defaults applied * * When using `ttl-backoff` mode, the core package will automatically create * a wait queue and the necessary exchanges and bindings. */ type ResolvedRetryOptions = NoneRetryOptions | ResolvedImmediateRequeueRetryOptions | ResolvedTtlBackoffRetryOptions; /** * Supported compression algorithms for message payloads. * * - `gzip`: GZIP compression (standard, widely supported, good compression ratio) * - `deflate`: DEFLATE compression (faster than gzip, slightly less compression) * * Compression is configured at runtime via PublishOptions when calling * AmqpClient.publish, not at publisher definition time. * * When compression is enabled, the message payload is compressed before publishing * and automatically decompressed when consuming. The `content-encoding` AMQP * message property is set to indicate the compression algorithm used. * * To disable compression, simply omit the `compression` option (it's optional). * * @example * ```typescript * // Define a publisher without compression configuration * const orderCreatedPublisher = definePublisher(exchange, message, { * routingKey: "order.created", * }); * * // Later, choose whether to compress at publish time * await client.publish("orderCreated", payload, { * compression: "gzip", * }); * ``` */ type CompressionAlgorithm = "gzip" | "deflate"; /** * Supported queue types in RabbitMQ. * * - `quorum`: Quorum queues (default, recommended) - Provide better durability and high-availability * using the Raft consensus algorithm. Best for most production use cases. * - `classic`: Classic queues - The traditional RabbitMQ queue type. Use only when you need * specific features not supported by quorum queues (e.g., non-durable queues, priority queues). * * Note: Quorum queues only support durable queues, and do not support exclusive, auto-deleting, or priority queues. * * @see https://www.rabbitmq.com/docs/quorum-queues * * @example * ```typescript * // Create a quorum queue (default, recommended) * const orderQueue = defineQueue('order-processing', { * type: 'quorum', // This is the default * }); * * // Create a classic queue (for special cases) * const tempQueue = defineQueue('temp-queue', { * type: 'classic', * durable: false, // Only supported with classic queues * }); * ``` */ type QueueType = "quorum" | "classic"; /** * Common queue options shared between quorum and classic queues. */ type BaseQueueOptions = { /** * Dead letter configuration for handling failed or rejected messages. */ deadLetter?: DeadLetterConfig; /** * Retry configuration for handling failed message processing. * * @example * ```typescript * // No retry * const orderQueue = defineQueue('order-processing', { * retry: { mode: 'none' }, * }); * * // Immediate-requeue mode * const orderQueue = defineQueue('order-processing', { * retry: { mode: 'immediate-requeue', maxRetries: 5 }, * }); * * // TTL-backoff mode with custom options * const orderQueue = defineQueue('order-processing', { * retry: { * mode: 'ttl-backoff', * maxRetries: 5, * initialDelayMs: 1000, * maxDelayMs: 30000, * }, * }); * ``` */ retry?: RetryOptions; /** * Additional AMQP arguments for advanced configuration. */ arguments?: Record; }; /** * Options for creating a quorum queue. * * Quorum queues do not support: * - `exclusive` - Use classic queues for connection-scoped queues * - `autoDelete` - Use classic queues for auto-deleting queues when consumers disconnect * - `maxPriority` - Use classic queues for priority queues * - `durable: false` - Use classic queues for non-durable queues * * Quorum queues provide native retry support for immediate-requeue retry mode: * - RabbitMQ tracks delivery count automatically via `x-delivery-count` header * - When the limit is exceeded, messages are dead-lettered (if DLX is configured) or dropped * - This is simpler than TTL-based retry and avoids head-of-queue blocking issues * * @example * ```typescript * const orderQueue = defineQueue('orders', { * type: 'quorum', * deadLetter: { exchange: dlx }, * retry: { mode: 'immediate-requeue', maxRetries: 3 } // Message dead-lettered after 3 retry attempts * }); * ``` */ type QuorumQueueOptions = BaseQueueOptions & { /** * Queue type: quorum (default, recommended) */ type?: "quorum"; /** * Quorum queues only support durable queues. */ durable?: true; /** * Quorum queues do not support exclusive mode. * Use type: 'classic' if you need exclusive queues. */ exclusive?: never; /** * Quorum queues do not support auto-delete mode. * Use type: 'classic' if you need auto-deleting queues. */ autoDelete?: never; /** * Quorum queues do not support priority queues. * Use type: 'classic' if you need priority queues. */ maxPriority?: never; }; /** * Options for creating a classic queue. * * Classic queues support all traditional RabbitMQ features including: * - `exclusive` - For connection-scoped queues * - `autoDelete` - For auto-deleting queues when consumers disconnect * - `maxPriority` - For priority queues * - `durable: false` - For non-durable queues * * @example * ```typescript * const priorityQueue = defineQueue('tasks', { * type: 'classic', * maxPriority: 10, * }); * ``` */ type ClassicQueueOptions = BaseQueueOptions & { /** * Queue type: classic (for special cases) */ type: "classic"; /** * If true, the queue survives broker restarts. Durable queues are persisted to disk. * @default true */ durable?: boolean; /** * If true, the queue can only be used by the declaring connection and is deleted when * that connection closes. Exclusive queues are private to the connection. */ exclusive?: boolean; /** * If true, the queue is deleted when the last consumer unsubscribes. */ autoDelete?: boolean; /** * Maximum priority level for priority queue (1-255, recommended: 1-10). * Sets x-max-priority argument. */ maxPriority?: number; }; /** * Options for defining a queue. Uses a discriminated union based on the `type` property * to enforce quorum queue constraints at compile time. * * - Quorum queues (default): Do not support `exclusive`, `autoDelete`, or `maxPriority` * - Classic queues: Support all options including `exclusive`, `autoDelete`, and `maxPriority` */ type DefineQueueOptions = QuorumQueueOptions | ClassicQueueOptions; /** * Options for defining a queue with a dead letter exchange. */ type DefineQueueOptionsWithDeadLetterExchange = DefineQueueOptions & { deadLetter: { exchange: TDlx; }; }; /** * Base definition of an AMQP exchange. * * An exchange receives messages from publishers and routes them to queues based on the exchange * type and routing rules. This type contains properties common to all exchange types. */ type BaseExchangeDefinition = { /** * The name of the exchange. Must be unique within the RabbitMQ virtual host. */ name: TName; /** * If true, the exchange survives broker restarts. Durable exchanges are persisted to disk. * @default true */ durable?: boolean; /** * If true, the exchange is deleted when all queues have finished using it. */ autoDelete?: boolean; /** * If true, the exchange cannot be directly published to by clients. * It can only receive messages from other exchanges via exchange-to-exchange bindings. */ internal?: boolean; /** * Additional AMQP arguments for advanced configuration. * Common arguments include alternate-exchange for handling unroutable messages. */ arguments?: Record; }; /** * A topic exchange definition. * * Topic exchanges route messages to queues based on routing key patterns with wildcards: * - `*` (star) matches exactly one word * - `#` (hash) matches zero or more words * * Words are separated by dots (e.g., `order.created.high-value`). * * @example * ```typescript * const ordersExchange: TopicExchangeDefinition = defineExchange('orders', { * type: 'topic', // This is the default type, so it can be omitted * }); * // Can be bound with patterns like 'order.*' or 'order.#' * ``` */ type TopicExchangeDefinition = BaseExchangeDefinition & { type: "topic"; }; /** * A direct exchange definition. * * Direct exchanges route messages to queues based on exact routing key matches. * This is ideal for point-to-point messaging where each message should go to specific queues. * * @example * ```typescript * const tasksExchange: DirectExchangeDefinition = defineExchange('tasks', { * type: 'direct', * }); * ``` */ type DirectExchangeDefinition = BaseExchangeDefinition & { type: "direct"; }; /** * A fanout exchange definition. * * Fanout exchanges broadcast all messages to all bound queues, ignoring routing keys. * This is the simplest exchange type for pub/sub messaging patterns. * * @example * ```typescript * const logsExchange: FanoutExchangeDefinition = defineExchange('logs', { * type: 'fanout', * }); * ``` */ type FanoutExchangeDefinition = BaseExchangeDefinition & { type: "fanout"; }; /** * A headers exchange definition. * * Headers exchanges route messages based on header values rather than routing keys. * This is useful for more complex routing scenarios where metadata is important. * * @example * ```typescript * const routesExchange: HeadersExchangeDefinition = defineExchange('routes', { * type: 'headers', * }); * ``` */ type HeadersExchangeDefinition = BaseExchangeDefinition & { type: "headers"; }; /** * Union type of all exchange definitions. * * Represents any type of AMQP exchange: topic, direct, fanout, headers. */ type ExchangeDefinition = TopicExchangeDefinition | DirectExchangeDefinition | FanoutExchangeDefinition | HeadersExchangeDefinition; /** * Configuration for dead letter exchange (DLX) on a queue. * * When a message in a queue is rejected, expires, or exceeds the queue length limit, * it can be automatically forwarded to a dead letter exchange for further processing * or storage. */ type DeadLetterConfig = { /** * The exchange to send dead-lettered messages to. * This exchange must be declared in the contract. */ exchange: ExchangeDefinition; /** * Optional routing key to use when forwarding messages to the dead letter exchange. * If not specified, the original message routing key is used. */ routingKey?: string; }; /** * Common properties shared by all queue definitions. */ type BaseQueueDefinition = { /** * The name of the queue. Must be unique within the RabbitMQ virtual host. */ name: TName; /** * Dead letter configuration for handling failed or rejected messages. * * When configured, messages that are rejected, expire, or exceed queue limits * will be automatically forwarded to the specified dead letter exchange. */ deadLetter?: DeadLetterConfig; /** * Retry configuration for handling failed message processing. * When the queue is created, defaults are applied. */ retry: ResolvedRetryOptions; /** * Additional AMQP arguments for advanced configuration. * * Common arguments include: * - `x-message-ttl`: Message time-to-live in milliseconds * - `x-expires`: Queue expiration time in milliseconds * - `x-max-length`: Maximum number of messages in the queue * - `x-max-length-bytes`: Maximum size of the queue in bytes */ arguments?: Record; }; /** * Definition of a quorum queue. * * Quorum queues provide better durability and high-availability using the Raft consensus algorithm. */ type QuorumQueueDefinition = BaseQueueDefinition & { /** * Queue type discriminator: quorum queue. */ type: "quorum"; /** * Quorum queues only support durable queues. */ durable: true; /** * Quorum queues do not support exclusive mode. * Use type: 'classic' if you need exclusive queues. */ exclusive?: never; /** * Quorum queues do not support auto-delete mode. * Use type: 'classic' if you need auto-deleting queues. */ autoDelete?: never; /** * Quorum queues do not support priority queues. * Use type: 'classic' if you need priority queues. */ maxPriority?: never; }; /** * Definition of a classic queue. * * Classic queues are the traditional RabbitMQ queue type. Use them when you need * specific features not supported by quorum queues (e.g., exclusive queues, auto-deleting queues, priority queues). */ type ClassicQueueDefinition = BaseQueueDefinition & { /** * Queue type discriminator: classic queue. */ type: "classic"; /** * If true, the queue survives broker restarts. Durable queues are persisted to disk. */ durable: boolean; /** * If true, the queue can only be used by the declaring connection and is deleted when * that connection closes. Exclusive queues are private to the connection. */ exclusive?: boolean; /** * If true, the queue is deleted when the last consumer unsubscribes. */ autoDelete?: boolean; /** * Maximum priority level for priority queue (1-255, recommended: 1-10). * Sets x-max-priority argument. */ maxPriority?: number; }; /** * Definition of an AMQP queue. * * A discriminated union based on queue type: * - `QuorumQueueDefinition`: For quorum queues (type: "quorum") * - `ClassicQueueDefinition`: For classic queues (type: "classic") * * Use `queue.type` as the discriminator to narrow the type. */ type QueueDefinition = QuorumQueueDefinition | ClassicQueueDefinition; /** * A queue with automatically generated TTL-backoff retry infrastructure. * * This type is returned by `defineQueue` when TTL-backoff retry is configured. * When passed to `defineContract`, the wait queue, exchanges, and bindings are * automatically added to the contract. * * @example * ```typescript * const exchange = defineExchange('orders'); * const queue = defineQueue('order-processing', { * retry: { mode: 'ttl-backoff', maxRetries: 5 }, * }); * // queue is QueueWithTtlBackoffInfrastructure * const message = defineMessage(z.object({ orderId: z.string() })); * const orderCreated = defineEventPublisher(exchange, message, { routingKey: 'order.created' }); * * // Wait queue, exchanges, and bindings are automatically extracted * const contract = defineContract({ * publishers: { orderCreated }, * consumers: { processOrder: defineEventConsumer(orderCreated, queue) }, * }); * ``` */ type QueueWithTtlBackoffInfrastructure = { /** * Discriminator to identify this as a queue with TTL-backoff infrastructure. * @internal */ __brand: "QueueWithTtlBackoffInfrastructure"; /** * The main queue definition. */ queue: QueueDefinition; /** * The wait queue for holding messages during backoff delay. */ waitQueue: QueueDefinition; /** * Wait exchange used to route failed messages to the wait queue. */ waitExchange: HeadersExchangeDefinition; /** * Retry exchange used to route messages to retry back to the main queue. */ retryExchange: HeadersExchangeDefinition; /** * Binding that routes failed messages to the wait queue. */ waitQueueBinding: QueueBindingDefinition; /** * Binding that routes messages to retry back to the main queue. */ retryQueueBinding: QueueBindingDefinition; }; /** * A queue entry that can be passed to `defineContract`. * * Can be either a plain queue definition or a queue with TTL-backoff infrastructure. */ type QueueEntry = QueueDefinition | QueueWithTtlBackoffInfrastructure; /** * A queue entry with a dead letter exchange. */ type QueueEntryWithDeadLetterExchange = QueueEntry & { deadLetter: { exchange: TDlx; }; }; /** * Definition of a message with typed payload and optional headers. * * @template TPayload - The Standard Schema v1 compatible schema for the message payload * @template THeaders - The Standard Schema v1 compatible schema for the message headers (optional) */ type MessageDefinition> | undefined = StandardSchemaV1> | undefined> = { /** * The payload schema for validating message content. * Must be a Standard Schema v1 compatible schema (Zod, Valibot, ArkType, etc.). */ payload: TPayload; /** * Optional headers schema for validating message metadata. * Must be a Standard Schema v1 compatible schema. */ headers?: THeaders; /** * Brief description of the message for documentation purposes. * Used in AsyncAPI specification generation. */ summary?: string; /** * Detailed description of the message for documentation purposes. * Used in AsyncAPI specification generation. */ description?: string; }; /** * Binding between a queue and an exchange. * * Defines how messages from an exchange should be routed to a queue. * For direct and topic exchanges, a routing key is required. * For fanout and headers exchanges, no routing key is needed. */ type QueueBindingDefinition = { /** Discriminator indicating this is a queue-to-exchange binding */type: "queue"; /** The queue that will receive messages */ queue: QueueDefinition; /** * Additional AMQP arguments for the binding. * Can be used for advanced routing scenarios with the headers exchange type. */ arguments?: Record; } & ({ /** Direct or topic exchange requiring a routing key */exchange: DirectExchangeDefinition | TopicExchangeDefinition; /** * The routing key pattern for message routing. * For direct exchanges: Must match exactly. * For topic exchanges: Can use wildcards (* for one word, # for zero or more words). */ routingKey: string; } | { /** Fanout or headers exchange (no routing key needed) */exchange: FanoutExchangeDefinition | HeadersExchangeDefinition; /** Fanout and headers exchanges don't use routing keys */ routingKey?: never; }); /** * Binding between two exchanges (exchange-to-exchange routing). * * Defines how messages should be forwarded from a source exchange to a destination exchange. * This allows for more complex routing topologies. * * @example * ```typescript * // Forward high-priority orders to a special processing exchange * const binding: ExchangeBindingDefinition = { * type: 'exchange', * source: ordersExchange, * destination: highPriorityExchange, * routingKey: 'order.high-priority.*' * }; * ``` */ type ExchangeBindingDefinition = { /** Discriminator indicating this is an exchange-to-exchange binding */type: "exchange"; /** The destination exchange that will receive forwarded messages */ destination: ExchangeDefinition; /** * Additional AMQP arguments for the binding. */ arguments?: Record; } & ({ /** Direct or topic source exchange requiring a routing key */source: DirectExchangeDefinition | TopicExchangeDefinition; /** * The routing key pattern for message routing. * Messages matching this pattern will be forwarded to the destination exchange. */ routingKey: string; } | { /** Fanout or headers source exchange (no routing key needed) */source: FanoutExchangeDefinition | HeadersExchangeDefinition; /** Fanout and headers exchanges don't use routing keys */ routingKey?: never; }); /** * Union type of all binding definitions. * * A binding can be either: * - Queue-to-exchange binding: Routes messages from an exchange to a queue * - Exchange-to-exchange binding: Forwards messages from one exchange to another */ type BindingDefinition = QueueBindingDefinition | ExchangeBindingDefinition; /** * Definition of a message publisher. * * A publisher sends messages to an exchange with automatic schema validation. * The message payload is validated against the schema before being sent to RabbitMQ. * * Compression can be optionally applied at publish time by specifying a compression * algorithm when calling the publish method. * * @template TMessage - The message definition with payload schema * * @example * ```typescript * const publisher: PublisherDefinition = { * exchange: ordersExchange, * message: orderMessage, * routingKey: 'order.created' * }; * ``` */ type PublisherDefinition = { /** The message definition including the payload schema */message: TMessage; } & ({ /** Direct or topic exchange requiring a routing key */exchange: DirectExchangeDefinition | TopicExchangeDefinition; /** * The routing key for message routing. * Determines which queues will receive the published message. */ routingKey: string; } | { /** Fanout or headers exchange (no routing key needed) */exchange: FanoutExchangeDefinition | HeadersExchangeDefinition; /** Fanout and headers exchanges don't use routing keys */ routingKey?: never; }); /** * Definition of a message consumer. * * A consumer receives and processes messages from a queue with automatic schema validation. * The message payload is validated against the schema before being passed to your handler. * If the message is compressed (indicated by the content-encoding header), it will be * automatically decompressed before validation. * * @template TMessage - The message definition with payload schema * * @example * ```typescript * const consumer: ConsumerDefinition = { * queue: orderProcessingQueue, * message: orderMessage * }; * ``` */ type ConsumerDefinition = { /** The queue to consume messages from */queue: QueueEntry; /** The message definition including the payload schema */ message: TMessage; }; /** * Base type for event publisher configuration. * * This is a simplified type used in ContractDefinition. The full generic type * is defined in the builder module. * * @see defineEventPublisher for creating event publishers */ type EventPublisherConfigBase = { __brand: "EventPublisherConfig"; exchange: ExchangeDefinition; message: MessageDefinition; routingKey: string | undefined; arguments?: Record; }; /** * Base type for command consumer configuration. * * This is a simplified type used in ContractDefinition. The full generic type * is defined in the builder module. * * @see defineCommandConsumer for creating command consumers */ type CommandConsumerConfigBase = { __brand: "CommandConsumerConfig"; consumer: ConsumerDefinition; binding: QueueBindingDefinition; exchange: ExchangeDefinition; queue: QueueEntry; message: MessageDefinition; routingKey: string | undefined; }; /** * Base type for event consumer result. * * This is a simplified type used in ContractDefinitionInput. The full generic type * is defined in the builder module. * * @see defineEventConsumer for creating event consumers */ type EventConsumerResultBase = { __brand: "EventConsumerResult"; consumer: ConsumerDefinition; binding: QueueBindingDefinition; exchange: ExchangeDefinition; queue: QueueEntry; exchangeBinding: ExchangeBindingDefinition | undefined; bridgeExchange: ExchangeDefinition | undefined; }; /** * Base type for bridged publisher configuration. * * A bridged publisher publishes to a bridge exchange, which forwards messages * to the target exchange via an exchange-to-exchange binding. * * @see defineCommandPublisher with bridgeExchange option */ type BridgedPublisherConfigBase = { __brand: "BridgedPublisherConfig"; publisher: PublisherDefinition; exchangeBinding: ExchangeBindingDefinition; bridgeExchange: ExchangeDefinition; targetExchange: ExchangeDefinition; }; /** * Typed error map for an RPC: error code → message definition validating the * error's `data` payload. * * Reuses {@link MessageDefinition} so error data gets the same Standard Schema * validation and AsyncAPI metadata (`summary` / `description`) as request and * response payloads. The `headers` slot of an error's message definition is * ignored — error replies carry the code in a fixed AMQP header instead. * * @see defineRpc for declaring errors on an RPC */ type RpcErrorMap = Record; /** * Definition of an RPC operation: a request/response pair flowing over a * request queue with replies routed back via direct reply-to. * * An RPC is bidirectional on both ends — the server consumes requests and * publishes responses; the client publishes requests and consumes responses — * so it has its own slot in the contract (`rpcs`) rather than being shoehorned * into `consumers` or `publishers`. * * @template TRequestMessage - The request message definition * @template TResponseMessage - The response message definition * @template TQueue - The request queue entry * @template TErrors - The typed error map (undefined when the RPC declares none) * * @see defineRpc for creating RPC definitions */ type RpcDefinition = { /** The queue that receives RPC requests. Replies are routed back via direct reply-to. */queue: TQueue; /** Schema for the request payload (validated on both publish and consume). */ request: TRequestMessage; /** Schema for the response payload (validated on both worker reply and client receive). */ response: TResponseMessage; /** * Typed business errors the handler may return via `Err(rpcError(code, data))`. * Error data is validated against the declared schema on the worker before * the error reply is published, and re-validated on the client when it * arrives. Business errors are replied and acked — never retried. */ errors?: TErrors; }; /** * Complete AMQP contract definition (output type). * * A contract brings together all AMQP resources into a single, type-safe definition. * It defines the complete messaging topology including exchanges, queues, bindings, * publishers, and consumers. * * The contract is used by: * - Clients (TypedAmqpClient) for type-safe message publishing * - Workers (TypedAmqpWorker) for type-safe message consumption * - AsyncAPI generator for documentation * * @example * ```typescript * const contract: ContractDefinition = { * exchanges: { * orders: ordersExchange, * }, * queues: { * orderProcessing: orderProcessingQueue, * }, * bindings: { * orderBinding: orderQueueBinding, * }, * publishers: { * orderCreated: orderCreatedPublisher, * }, * consumers: { * processOrder: processOrderConsumer, * }, * }; * ``` */ type ContractDefinition = { /** * Named exchange definitions. * Each key becomes available as a named resource in the contract. */ exchanges?: Record; /** * Named queue definitions. * Each key becomes available as a named resource in the contract. * * When a queue has TTL-backoff retry configured, pass the `QueueWithTtlBackoffInfrastructure` * object returned by `defineQueue`. The wait queue, exchanges, and bindings will be automatically added. */ queues?: Record; /** * Named binding definitions. * Bindings can be queue-to-exchange or exchange-to-exchange. */ bindings?: Record; /** * Named publisher definitions. * Each key becomes a method on the TypedAmqpClient for publishing messages. * The method will be fully typed based on the message schema. */ publishers?: Record; /** * Named consumer definitions. * Each key requires a corresponding handler in the TypedAmqpWorker. * The handler will be fully typed based on the message schema. */ consumers?: Record; /** * Named RPC definitions. Each key gets: * - A handler in the TypedAmqpWorker that returns the typed response. * - A `client.call(name, request, options)` method on the TypedAmqpClient. * * RPC entries do not appear in `publishers` or `consumers` because each * end of an RPC plays both roles (publisher of one direction, consumer of * the other). */ rpcs?: Record; }; /** * Publisher entry that can be passed to defineContract's publishers section. * * Can be either: * - A plain PublisherDefinition from definePublisher * - An EventPublisherConfig from defineEventPublisher (auto-extracted to publisher) * - An BridgedPublisherConfig from defineCommandPublisher (auto-extracted to publisher) */ type PublisherEntry = PublisherDefinition | EventPublisherConfigBase | BridgedPublisherConfigBase; /** * Consumer entry that can be passed to defineContract's consumers section. * * Can be either: * - A plain ConsumerDefinition from defineConsumer * - An EventConsumerResult from defineEventConsumer (binding auto-extracted) * - A CommandConsumerConfig from defineCommandConsumer (binding auto-extracted) */ type ConsumerEntry = ConsumerDefinition | EventConsumerResultBase | CommandConsumerConfigBase; /** * Contract definition input type with automatic extraction of event/command patterns. * * Users only define publishers and consumers. Exchanges, queues, and bindings are * automatically extracted from these definitions. * * @example * ```typescript * const contract = defineContract({ * publishers: { * // EventPublisherConfig → auto-extracted to publisher * orderCreated: defineEventPublisher(ordersExchange, orderMessage, { routingKey: "order.created" }), * }, * consumers: { * // CommandConsumerConfig → auto-extracted to consumer + binding * processOrder: defineCommandConsumer(orderQueue, ordersExchange, orderMessage, { routingKey: "order.process" }), * // EventConsumerResult → auto-extracted to consumer + binding * notify: defineEventConsumer(orderCreatedEvent, notificationQueue), * }, * }); * ``` * * @see defineContract - Processes this input and returns a ContractDefinition */ type ContractDefinitionInput = { /** * Named publisher definitions. * * Can accept: * - PublisherDefinition from definePublisher * - EventPublisherConfig from defineEventPublisher (auto-extracted to publisher) */ publishers?: Record; /** * Named consumer definitions. * * Can accept: * - ConsumerDefinition from defineConsumer * - EventConsumerResult from defineEventConsumer (binding auto-extracted) * - CommandConsumerConfig from defineCommandConsumer (binding auto-extracted) */ consumers?: Record; /** * Named RPC definitions from `defineRpc`. Each entry contributes its queue * (and DLX if any) to the contract topology and exposes a typed * `client.call(name, ...)` / worker handler pair. */ rpcs?: Record; }; /** * Extract the exchange from a publisher entry. * @internal */ type ExtractPublisherExchange = T extends BridgedPublisherConfigBase ? T["bridgeExchange"] : T extends EventPublisherConfigBase ? T["exchange"] : T extends PublisherDefinition ? T["exchange"] : never; /** * Extract the QueueDefinition from a QueueEntry type. * For QueueWithTtlBackoffInfrastructure, returns the inner queue definition. * For QueueDefinition, returns as-is. * For complex intersections, falls back to extracting TName from QueueEntry. * @internal */ type ExtractQueueFromEntry = T extends QueueWithTtlBackoffInfrastructure ? QueueDefinition : T extends QueueDefinition ? QueueDefinition : T extends QueueEntry ? QueueDefinition : QueueDefinition; /** * Extract the dead letter exchange from a QueueEntry type. * Handles both plain queue entries and those with DLX intersection from defineQueue overloads. * @internal */ type ExtractDlxFromEntry = T extends { deadLetter: { exchange: infer E extends ExchangeDefinition; }; } ? E : T extends QueueWithTtlBackoffInfrastructure ? T["queue"] extends { deadLetter: { exchange: infer E extends ExchangeDefinition; }; } ? E : never : never; /** * Extract the queue from a consumer entry. * @internal */ type ExtractConsumerQueue = T extends EventConsumerResultBase ? T["queue"] : T extends CommandConsumerConfigBase ? T["queue"] : T extends ConsumerDefinition ? T["queue"] : never; /** * Extract the exchange from a consumer entry (from binding). * @internal */ type ExtractConsumerExchange = T extends EventConsumerResultBase ? T["exchange"] : T extends CommandConsumerConfigBase ? T["exchange"] : never; /** * Extract the binding from a consumer entry. * @internal */ type ExtractConsumerBinding = T extends EventConsumerResultBase ? T["binding"] : T extends CommandConsumerConfigBase ? T["binding"] : never; /** * Check if a consumer entry has a binding. * @internal */ type HasBinding = T extends EventConsumerResultBase ? true : T extends CommandConsumerConfigBase ? true : false; /** * Extract exchanges from all publishers in a contract. * @internal */ type ExtractExchangesFromPublishers> = { [K in keyof TPublishers as ExtractPublisherExchange["name"]]: ExtractPublisherExchange }; /** * Extract exchanges from all consumers in a contract. * @internal */ type ExtractExchangesFromConsumers> = { [K in keyof TConsumers as ExtractConsumerExchange extends ExchangeDefinition ? ExtractConsumerExchange["name"] : never]: ExtractConsumerExchange extends ExchangeDefinition ? ExtractConsumerExchange : never }; /** * Extract the dead letter exchange from a consumer entry. * @internal */ type ExtractDeadLetterExchange = ExtractDlxFromEntry; /** * Extract dead letter exchanges from all consumers in a contract. * @internal */ type ExtractDeadLetterExchangesFromConsumers> = { [K in keyof TConsumers as ExtractDeadLetterExchange extends never ? never : ExtractDeadLetterExchange["name"]]: ExtractDeadLetterExchange }; /** * Extract queues from all consumers in a contract. * @internal */ type ExtractQueuesFromConsumers> = { [K in keyof TConsumers as ExtractQueueFromEntry>["name"]]: ExtractConsumerQueue }; /** * Extract bindings from all consumers in a contract. * @internal */ type ExtractBindingsFromConsumers> = { [K in keyof TConsumers as HasBinding extends true ? `${K & string}Binding` : never]: ExtractConsumerBinding }; /** * Extract the consumer definition from a consumer entry. * @internal */ type ExtractConsumerDefinition = T extends EventConsumerResultBase ? T["consumer"] : T extends CommandConsumerConfigBase ? T["consumer"] : T extends ConsumerDefinition ? T : never; /** * Extract consumer definitions from all consumers in a contract. * @internal */ type ExtractConsumerDefinitions> = { [K in keyof TConsumers]: ExtractConsumerDefinition }; /** * Extract the publisher definition from a publisher entry. * @internal */ type ExtractPublisherDefinition = T extends BridgedPublisherConfigBase ? T["publisher"] : T extends EventPublisherConfigBase ? PublisherDefinition & (T["exchange"] extends DirectExchangeDefinition | TopicExchangeDefinition ? { exchange: T["exchange"]; routingKey: T["routingKey"] & string; } : { exchange: T["exchange"]; routingKey?: never; }) : T extends PublisherDefinition ? T : never; /** * Extract publisher definitions from all publishers in a contract. * @internal */ type ExtractPublisherDefinitions> = { [K in keyof TPublishers]: ExtractPublisherDefinition }; /** * Extract the bridge exchange from a consumer entry (when bridgeExchange is set). * @internal */ type ExtractBridgeExchangeFromConsumer = T extends EventConsumerResultBase ? T["bridgeExchange"] extends ExchangeDefinition ? T["bridgeExchange"] : never : never; /** * Extract bridge exchanges from all consumers in a contract. * @internal */ type ExtractBridgeExchangesFromConsumers> = { [K in keyof TConsumers as ExtractBridgeExchangeFromConsumer extends never ? never : ExtractBridgeExchangeFromConsumer["name"]]: ExtractBridgeExchangeFromConsumer }; /** * Extract the target exchange from a bridged publisher entry. * @internal */ type ExtractTargetExchangeFromPublisher = T extends BridgedPublisherConfigBase ? T["targetExchange"] : never; /** * Extract target exchanges from all publishers in a contract. * @internal */ type ExtractTargetExchangesFromPublishers> = { [K in keyof TPublishers as ExtractTargetExchangeFromPublisher extends never ? never : ExtractTargetExchangeFromPublisher["name"]]: ExtractTargetExchangeFromPublisher }; /** * Check if a consumer entry has an exchange binding (e2e). * @internal */ type HasConsumerExchangeBinding = T extends EventConsumerResultBase ? T["exchangeBinding"] extends ExchangeBindingDefinition ? true : false : false; /** * Extract the exchange binding from a consumer entry. * @internal */ type ExtractConsumerExchangeBinding = T extends EventConsumerResultBase ? T["exchangeBinding"] extends ExchangeBindingDefinition ? T["exchangeBinding"] : never : never; /** * Extract exchange bindings from all consumers in a contract. * @internal */ type ExtractExchangeBindingsFromConsumers> = { [K in keyof TConsumers as HasConsumerExchangeBinding extends true ? `${K & string}ExchangeBinding` : never]: ExtractConsumerExchangeBinding }; /** * Check if a publisher entry has an exchange binding (bridged). * @internal */ type HasPublisherExchangeBinding = T extends BridgedPublisherConfigBase ? true : false; /** * Extract the exchange binding from a bridged publisher entry. * @internal */ type ExtractPublisherExchangeBinding = T extends BridgedPublisherConfigBase ? T["exchangeBinding"] : never; /** * Extract exchange bindings from all publishers in a contract. * @internal */ type ExtractExchangeBindingsFromPublishers> = { [K in keyof TPublishers as HasPublisherExchangeBinding extends true ? `${K & string}ExchangeBinding` : never]: ExtractPublisherExchangeBinding }; /** * Extract queues from all RPC entries in a contract. * @internal */ type ExtractQueuesFromRpcs> = { [K in keyof TRpcs as ExtractQueueFromEntry["name"]]: TRpcs[K]["queue"] }; /** * Extract dead letter exchanges from all RPC entries in a contract. * @internal */ type ExtractDeadLetterExchangesFromRpcs> = { [K in keyof TRpcs as ExtractDlxFromEntry extends never ? never : ExtractDlxFromEntry["name"]]: ExtractDlxFromEntry }; /** * Contract output type with all resources extracted and properly typed. * * This type represents the fully expanded contract with: * - exchanges: Extracted from publishers and consumer bindings * - queues: Extracted from consumers * - bindings: Extracted from event/command consumers * - publishers: Normalized publisher definitions * - consumers: Normalized consumer definitions */ type ContractOutput = { exchanges: (TContract["publishers"] extends Record ? ExtractExchangesFromPublishers : {}) & (TContract["consumers"] extends Record ? ExtractExchangesFromConsumers : {}) & (TContract["consumers"] extends Record ? ExtractDeadLetterExchangesFromConsumers : {}) & (TContract["consumers"] extends Record ? ExtractBridgeExchangesFromConsumers : {}) & (TContract["publishers"] extends Record ? ExtractTargetExchangesFromPublishers : {}) & (TContract["rpcs"] extends Record ? ExtractDeadLetterExchangesFromRpcs : {}); queues: (TContract["consumers"] extends Record ? ExtractQueuesFromConsumers : {}) & (TContract["rpcs"] extends Record ? ExtractQueuesFromRpcs : {}); bindings: (TContract["consumers"] extends Record ? ExtractBindingsFromConsumers : {}) & (TContract["consumers"] extends Record ? ExtractExchangeBindingsFromConsumers : {}) & (TContract["publishers"] extends Record ? ExtractExchangeBindingsFromPublishers : {}); publishers: TContract["publishers"] extends Record ? ExtractPublisherDefinitions : {}; consumers: TContract["consumers"] extends Record ? ExtractConsumerDefinitions : {}; rpcs: TContract["rpcs"] extends Record ? TContract["rpcs"] : {}; }; /** * Extract publisher names from a contract. * * This utility type extracts the keys of all publishers defined in a contract. * It's used internally for type inference in the TypedAmqpClient. * * @template TContract - The contract definition * @returns Union of publisher names, or never if no publishers defined * * @example * ```typescript * type PublisherNames = InferPublisherNames; * // Result: 'orderCreated' | 'orderUpdated' | 'orderCancelled' * ``` */ type InferPublisherNames = TContract["publishers"] extends Record ? keyof TContract["publishers"] : never; /** * Extract consumer names from a contract. * * This utility type extracts the keys of all consumers defined in a contract. * It's used internally for type inference in the TypedAmqpWorker. * * @template TContract - The contract definition * @returns Union of consumer names, or never if no consumers defined * * @example * ```typescript * type ConsumerNames = InferConsumerNames; * // Result: 'processOrder' | 'sendNotification' | 'updateInventory' * ``` */ type InferConsumerNames = TContract["consumers"] extends Record ? keyof TContract["consumers"] : never; /** * Extract RPC names from a contract. * * Each name in this union has a typed worker handler and a `client.call(name, ...)` * method. RPC names are disjoint from `InferConsumerNames` and `InferPublisherNames`. * * @template TContract - The contract definition * @returns Union of RPC names, or never if no RPCs defined */ type InferRpcNames = TContract["rpcs"] extends Record ? keyof TContract["rpcs"] : never; //#endregion //#region src/builder/exchange.d.ts /** * Define a topic exchange. * * A topic exchange routes messages to queues based on routing key patterns. * Routing keys can use wildcards: `*` matches one word, `#` matches zero or more words. * This exchange type is ideal for flexible message routing based on hierarchical topics. * * @param name - The name of the exchange * @param options - Optional exchange configuration * @param options.type - Exchange type (must be "topic", or omitted for default topic exchange) * @param options.durable - If true, the exchange survives broker restarts (default: true) * @param options.autoDelete - If true, the exchange is deleted when no queues are bound * @param options.internal - If true, the exchange cannot be directly published to * @param options.arguments - Additional AMQP arguments for the exchange * @returns A topic exchange definition * * @example * ```typescript * const ordersExchange = defineExchange('orders', { type: 'topic' }); * * // Or omit type for default topic exchange * const ordersExchange = defineExchange('orders'); * ``` */ declare function defineExchange(name: TName, options?: { type?: "topic"; } & Omit): TopicExchangeDefinition; /** * Define a direct exchange. * * A direct exchange routes messages to queues based on exact routing key matches. * This exchange type is ideal for point-to-point messaging. * * @param name - The name of the exchange * @param options - Exchange configuration * @param options.type - Exchange type (must be "direct") * @param options.durable - If true, the exchange survives broker restarts (default: true) * @param options.autoDelete - If true, the exchange is deleted when no queues are bound * @param options.internal - If true, the exchange cannot be directly published to * @param options.arguments - Additional AMQP arguments for the exchange * @returns A direct exchange definition * * @example * ```typescript * const tasksExchange = defineExchange('tasks', { type: 'direct' }); * ``` */ declare function defineExchange(name: TName, options: { type: "direct"; } & Omit): DirectExchangeDefinition; /** * Define a fanout exchange. * * A fanout exchange routes messages to all bound queues without considering routing keys. * This exchange type is ideal for broadcasting messages to multiple consumers. * * @param name - The name of the exchange * @param options - Exchange configuration * @param options.type - Exchange type (must be "fanout") * @param options.durable - If true, the exchange survives broker restarts (default: true) * @param options.autoDelete - If true, the exchange is deleted when no queues are bound * @param options.internal - If true, the exchange cannot be directly published to * @param options.arguments - Additional AMQP arguments for the exchange * @returns A fanout exchange definition * * @example * ```typescript * const logsExchange = defineExchange('logs', { type: 'fanout' }); * ``` */ declare function defineExchange(name: TName, options: { type: "fanout"; } & Omit): FanoutExchangeDefinition; /** * Define a headers exchange. * * A headers exchange routes messages to all bound queues based on header matching. * This exchange type is ideal for complex routing scenarios. * * @param name - The name of the exchange * @param options - Exchange configuration * @param options.type - Exchange type (must be "headers") * @param options.durable - If true, the exchange survives broker restarts (default: true) * @param options.autoDelete - If true, the exchange is deleted when no queues are bound * @param options.internal - If true, the exchange cannot be directly published to * @param options.arguments - Additional AMQP arguments for the exchange * @returns A headers exchange definition * * @example * ```typescript * const routesExchange = defineExchange('routes', { type: 'headers' }); * ``` */ declare function defineExchange(name: TName, options: { type: "headers"; } & Omit): HeadersExchangeDefinition; //#endregion //#region src/builder/message.d.ts /** * Define a message definition with payload and optional headers/metadata. * * A message definition specifies the schema for message payloads and headers using * Standard Schema v1 compatible libraries (Zod, Valibot, ArkType, etc.). * The schemas are used for automatic validation when publishing or consuming messages. * * @param payload - The payload schema (must be Standard Schema v1 compatible) * @param options - Optional message metadata * @param options.headers - Optional header schema for message headers * @param options.summary - Brief description for documentation (used in AsyncAPI generation) * @param options.description - Detailed description for documentation (used in AsyncAPI generation) * @returns A message definition with inferred types * * @example * ```typescript * import { z } from 'zod'; * * const orderMessage = defineMessage( * z.object({ * orderId: z.string().uuid(), * customerId: z.string().uuid(), * amount: z.number().positive(), * items: z.array(z.object({ * productId: z.string(), * quantity: z.number().int().positive(), * })), * }), * { * summary: 'Order created event', * description: 'Emitted when a new order is created in the system' * } * ); * ``` */ declare function defineMessage> | undefined = undefined>(payload: TPayload, options?: { headers?: THeaders; summary?: string; description?: string; }): MessageDefinition; //#endregion //#region src/builder/queue.d.ts /** * Define an AMQP queue. * * A queue stores messages until they are consumed by workers. Queues can be bound to exchanges * to receive messages based on routing rules. * * By default, queues are created as quorum queues which provide better durability and * high-availability. Use `type: 'classic'` for special cases like non-durable queues * or priority queues. * * @param name - The name of the queue * @param options - Optional queue configuration * @param options.type - Queue type: 'quorum' (default, recommended) or 'classic' * @param options.durable - If true, the queue survives broker restarts. Quorum queues only support durable queues (default: true) * @param options.exclusive - If true, the queue can only be used by the declaring connection and is deleted when that connection closes. Only supported with classic queues. * @param options.autoDelete - If true, the queue is deleted when the last consumer unsubscribes. Only supported with classic queues. * @param options.maxPriority - Maximum priority level for priority queue (1-255, recommended: 1-10). Only supported with classic queues. * @param options.deadLetter - Dead letter configuration for handling failed messages * @param options.retry - Retry configuration for handling failed message processing * @param options.arguments - Additional AMQP arguments (e.g., x-message-ttl) * @returns A queue definition * * @example * ```typescript * // Quorum queue (default, recommended for production) * const orderQueue = defineQueue('order-processing'); * * // Explicit quorum queue with dead letter exchange * const dlx = defineExchange('orders-dlx'); * const orderQueueWithDLX = defineQueue('order-processing', { * type: 'quorum', * deadLetter: { * exchange: dlx, * routingKey: 'order.failed' * }, * arguments: { * 'x-message-ttl': 86400000, // 24 hours * } * }); * * // Classic queue (for special cases) * const tempQueue = defineQueue('temp-queue', { * type: 'classic', * durable: false, * autoDelete: true, * }); * * // Priority queue (requires classic type) * const taskQueue = defineQueue('urgent-tasks', { * type: 'classic', * maxPriority: 10, * }); * * // Queue with TTL-backoff retry (returns infrastructure automatically) * const dlx = defineExchange('orders-dlx', { type: 'direct' }); * const orderQueue = defineQueue('order-processing', { * deadLetter: { exchange: dlx }, * retry: { mode: 'ttl-backoff', maxRetries: 5 }, * }); * // orderQueue is QueueWithTtlBackoffInfrastructure, pass directly to defineContract * ``` */ declare function defineQueue(name: TName, options: DefineQueueOptionsWithDeadLetterExchange): QueueEntryWithDeadLetterExchange; declare function defineQueue(name: TName, options?: DefineQueueOptions): QueueEntry; //#endregion //#region src/builder/queue-utils.d.ts /** * Extract the plain QueueDefinition from a QueueEntry. * * **Why this function exists:** * When you configure a queue with TTL-backoff retry, * `defineQueue` returns a wrapper object that includes * the main queue, wait queue, headers exchanges, and bindings. This function extracts the underlying * queue definition so you can access properties like `name`, `type`, etc. * * **When to use:** * - When you need to access queue properties (name, type, etc.) * - When passing a queue to functions that expect a plain QueueDefinition * - Works safely on both plain queues and infrastructure wrappers * * **How it works:** * - If the entry is a `QueueWithTtlBackoffInfrastructure`, returns `entry.queue` * - Otherwise, returns the entry as-is (it's already a plain QueueDefinition) * * @param entry - The queue entry (either plain QueueDefinition or QueueWithTtlBackoffInfrastructure) * @returns The plain QueueDefinition * * @example * ```typescript * import { defineQueue, extractQueue } from '@amqp-contract/contract'; * * // TTL-backoff queue returns a wrapper * const orderQueue = defineQueue('orders', { * retry: { mode: 'ttl-backoff', maxRetries: 3 }, * }); * * // Use extractQueue to access the queue name * const queueName = extractQueue(orderQueue).name; // 'orders' * * // Also works safely on plain queues * const plainQueue = defineQueue('simple', { type: 'quorum', retry: { mode: 'immediate-requeue' } }); * const plainName = extractQueue(plainQueue).name; // 'simple' * * // Access other properties * const queueDef = extractQueue(orderQueue); * console.log(queueDef.name); // 'orders' * console.log(queueDef.type); // 'quorum' * ``` * * @see isQueueWithTtlBackoffInfrastructure - Type guard to check if extraction is needed */ declare function extractQueue(entry: T): ExtractQueueFromEntry; //#endregion //#region src/builder/binding.d.ts /** * Define a binding between a queue and a fanout or headers exchange. * * Binds a queue to a fanout or headers exchange (no routing key needed). * Fanout and headers exchanges ignore routing keys, so this overload doesn't require one. * * @param queue - The queue definition or queue with infrastructure to bind * @param exchange - The fanout or headers exchange definition * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments for the binding * @returns A queue binding definition * * @example * ```typescript * const logsQueue = defineQueue('logs-queue'); * const logsExchange = defineExchange('logs', { type: 'fanout' }); * * const binding = defineQueueBinding(logsQueue, logsExchange); * ``` */ declare function defineQueueBinding(queue: QueueEntry, exchange: FanoutExchangeDefinition | HeadersExchangeDefinition, options?: Omit, "type" | "queue" | "exchange" | "routingKey">): Extract; /** * Define a binding between a queue and a direct or topic exchange. * * Binds a queue to an exchange with a specific routing key pattern. * Messages are only routed to the queue if the routing key matches the pattern. * * For direct exchanges: The routing key must match exactly. * For topic exchanges: The routing key can include wildcards: * - `*` matches exactly one word * - `#` matches zero or more words * * @param queue - The queue definition or queue with infrastructure to bind * @param exchange - The direct or topic exchange definition * @param options - Binding configuration (routingKey is required) * @param options.routingKey - The routing key pattern for message routing * @param options.arguments - Additional AMQP arguments for the binding * @returns A queue binding definition * * @example * ```typescript * const orderQueue = defineQueue('order-processing'); * const ordersExchange = defineExchange('orders'); * * // Bind with exact routing key * const binding = defineQueueBinding(orderQueue, ordersExchange, { * routingKey: 'order.created' * }); * * // Bind with wildcard pattern * const allOrdersBinding = defineQueueBinding(orderQueue, ordersExchange, { * routingKey: 'order.*' // Matches order.created, order.updated, etc. * }); * ``` */ declare function defineQueueBinding(queue: QueueEntry, exchange: DirectExchangeDefinition | TopicExchangeDefinition, options: Omit, "type" | "queue" | "exchange">): Extract; /** * Define a binding between two exchanges (exchange-to-exchange routing). * * Binds a destination exchange to a fanout or headers source exchange. * Messages published to the source exchange will be forwarded to the destination exchange. * Fanout and headers exchanges ignore routing keys, so this overload doesn't require one. * * @param destination - The destination exchange definition * @param source - The fanout or headers source exchange definition * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments for the binding * @returns An exchange binding definition * * @example * ```typescript * const sourceExchange = defineExchange('logs', { type: 'fanout' }); * const destExchange = defineExchange('all-logs', { type: 'fanout' }); * * const binding = defineExchangeBinding(destExchange, sourceExchange); * ``` */ declare function defineExchangeBinding(destination: ExchangeDefinition, source: FanoutExchangeDefinition | HeadersExchangeDefinition, options?: Omit, "type" | "source" | "destination" | "routingKey">): Extract; /** * Define a binding between two exchanges (exchange-to-exchange routing). * * Binds a destination exchange to a direct or topic source exchange with a routing key pattern. * Messages are forwarded from source to destination only if the routing key matches the pattern. * * @param destination - The destination exchange definition * @param source - The direct or topic source exchange definition * @param options - Binding configuration (routingKey is required) * @param options.routingKey - The routing key pattern for message routing * @param options.arguments - Additional AMQP arguments for the binding * @returns An exchange binding definition * * @example * ```typescript * const ordersExchange = defineExchange('orders'); * const importantExchange = defineExchange('important-orders'); * * // Forward only high-value orders * const binding = defineExchangeBinding(importantExchange, ordersExchange, { * routingKey: 'order.high-value.*' * }); * ``` */ declare function defineExchangeBinding(destination: ExchangeDefinition, source: DirectExchangeDefinition | TopicExchangeDefinition, options: Omit, "type" | "source" | "destination">): Extract; //#endregion //#region src/builder/publisher.d.ts /** * Define a message publisher for a fanout or headers exchange. * * A publisher sends messages to an exchange. For fanout exchanges, messages are broadcast * to all bound queues regardless of routing key, so no routing key is required. For headers exchanges, * routing is based on message headers rather than routing keys, so no routing key is required either. * * The message schema is validated when publishing to ensure type safety. * * **Which pattern to use:** * * | Pattern | Best for | Description | * |---------|----------|-------------| * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency | * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it | * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it | * * Use `defineEventPublisher` when: * - One publisher feeds multiple consumers * - You want automatic schema consistency between publisher and consumers * - You're building event-driven architectures * * @param exchange - The fanout or headers exchange definition to publish to * @param message - The message definition with payload schema * @param options - Optional publisher configuration * @returns A publisher definition with inferred message types * * @example * ```typescript * import { z } from 'zod'; * * const logsExchange = defineExchange('logs', { type: 'fanout' }); * const logMessage = defineMessage( * z.object({ * level: z.enum(['info', 'warn', 'error']), * message: z.string(), * timestamp: z.string().datetime(), * }) * ); * * const logPublisher = definePublisher(logsExchange, logMessage); * ``` * * @see defineEventPublisher - For event-driven patterns with automatic schema consistency * @see defineCommandConsumer - For task queue patterns with automatic schema consistency */ declare function definePublisher(exchange: FanoutExchangeDefinition | HeadersExchangeDefinition, message: TMessage, options?: Omit, { exchange: FanoutExchangeDefinition | HeadersExchangeDefinition; }>, "exchange" | "message" | "routingKey">): Extract, { exchange: FanoutExchangeDefinition | HeadersExchangeDefinition; }>; /** * Define a message publisher for a direct or topic exchange. * * A publisher sends messages to an exchange with a specific routing key. * The routing key determines which queues receive the message. * * The message schema is validated when publishing to ensure type safety. * * **Which pattern to use:** * * | Pattern | Best for | Description | * |---------|----------|-------------| * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency | * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it | * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it | * * Use `defineEventPublisher` when: * - One publisher feeds multiple consumers * - You want automatic schema consistency between publisher and consumers * - You're building event-driven architectures * * @param exchange - The direct or topic exchange definition to publish to * @param message - The message definition with payload schema * @param options - Publisher configuration (routingKey is required) * @param options.routingKey - The routing key for message routing * @returns A publisher definition with inferred message types * * @example * ```typescript * import { z } from 'zod'; * * const ordersExchange = defineExchange('orders'); * const orderMessage = defineMessage( * z.object({ * orderId: z.string().uuid(), * amount: z.number().positive(), * }), * { * summary: 'Order created event', * description: 'Emitted when a new order is created' * } * ); * * const orderCreatedPublisher = definePublisher(ordersExchange, orderMessage, { * routingKey: 'order.created' * }); * ``` * * @see defineEventPublisher - For event-driven patterns with automatic schema consistency * @see defineCommandConsumer - For task queue patterns with automatic schema consistency */ declare function definePublisher(exchange: DirectExchangeDefinition | TopicExchangeDefinition, message: TMessage, options: Omit, { exchange: DirectExchangeDefinition | TopicExchangeDefinition; }>, "exchange" | "message">): Extract, { exchange: DirectExchangeDefinition | TopicExchangeDefinition; }>; //#endregion //#region src/builder/consumer.d.ts /** * Extract the ConsumerDefinition from any ConsumerEntry type. * * Handles the following entry types: * - ConsumerDefinition: returned as-is * - EventConsumerResult: returns the nested `.consumer` property * - CommandConsumerConfig: returns the nested `.consumer` property * * Use this function when you need to access the underlying ConsumerDefinition * from a consumer entry that may have been created with defineEventConsumer * or defineCommandConsumer. * * @param entry - The consumer entry to extract from * @returns The underlying ConsumerDefinition * * @example * ```typescript * // Works with plain ConsumerDefinition * const consumer1 = defineConsumer(queue, message); * extractConsumer(consumer1).queue.name; // "my-queue" * * // Works with EventConsumerResult * const consumer2 = defineEventConsumer(eventPublisher, queue); * extractConsumer(consumer2).queue.name; // "my-queue" * * // Works with CommandConsumerConfig * const consumer3 = defineCommandConsumer(queue, exchange, message, { routingKey: "cmd" }); * extractConsumer(consumer3).queue.name; // "my-queue" * ``` */ declare function extractConsumer(entry: ConsumerEntry): ConsumerDefinition; /** * Define a message consumer. * * A consumer receives and processes messages from a queue. The message schema is validated * automatically when messages are consumed, ensuring type safety for your handlers. * * Consumers are associated with a specific queue and message type. When you create a worker * with this consumer, it will process messages from the queue according to the schema. * * **Which pattern to use:** * * | Pattern | Best for | Description | * |---------|----------|-------------| * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency | * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it | * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it | * * Use `defineCommandConsumer` when: * - One consumer receives from multiple publishers * - You want automatic schema consistency between consumer and publishers * - You're building task queue or command patterns * * @param queue - The queue definition to consume from * @param message - The message definition with payload schema * @param options - Optional consumer configuration * @returns A consumer definition with inferred message types * * @example * ```typescript * import { z } from 'zod'; * * const orderQueue = defineQueue('order-processing'); * const orderMessage = defineMessage( * z.object({ * orderId: z.string().uuid(), * customerId: z.string().uuid(), * amount: z.number().positive(), * }) * ); * * const processOrderConsumer = defineConsumer(orderQueue, orderMessage); * * // Later, when creating a worker, you'll provide a handler for this consumer: * // const worker = await TypedAmqpWorker.create({ * // contract, * // handlers: { * // processOrder: async (message) => { * // // message is automatically typed based on the schema * // console.log(message.orderId); // string * // } * // }, * // connection * // }); * ``` * * @see defineCommandConsumer - For task queue patterns with automatic schema consistency * @see defineEventPublisher - For event-driven patterns with automatic schema consistency */ declare function defineConsumer(queue: QueueEntry, message: TMessage, options?: Omit, "queue" | "message">): ConsumerDefinition; //#endregion //#region src/builder/contract.d.ts /** * Define an AMQP contract. * * A contract is the central definition of your AMQP messaging topology. It brings together * publishers and consumers in a single, type-safe definition. Exchanges, queues, and bindings * are automatically extracted from publishers and consumers. * * The contract is used by both clients (for publishing) and workers (for consuming) to ensure * type safety throughout your messaging infrastructure. TypeScript will infer all message types * and publisher/consumer names from the contract. * * @param definition - The contract definition containing publishers and consumers * @param definition.publishers - Named publisher definitions for sending messages * @param definition.consumers - Named consumer definitions for receiving messages * @returns The contract definition with fully inferred exchanges, queues, bindings, publishers, and consumers * * @example * ```typescript * import { * defineContract, * defineExchange, * defineQueue, * defineEventPublisher, * defineEventConsumer, * defineMessage, * } from '@amqp-contract/contract'; * import { z } from 'zod'; * * // Define resources * const ordersExchange = defineExchange('orders'); * const dlx = defineExchange('orders-dlx', { type: 'direct' }); * const orderQueue = defineQueue('order-processing', { * deadLetter: { exchange: dlx }, * retry: { mode: 'immediate-requeue', maxRetries: 3 }, * }); * const orderMessage = defineMessage( * z.object({ * orderId: z.string(), * amount: z.number(), * }) * ); * * // Define event publisher * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, { * routingKey: 'order.created', * }); * * // Compose contract - exchanges, queues, bindings are auto-extracted * export const contract = defineContract({ * publishers: { * orderCreated: orderCreatedEvent, * }, * consumers: { * processOrder: defineEventConsumer(orderCreatedEvent, orderQueue), * }, * }); * * // TypeScript now knows: * // - contract.exchanges.orders, contract.exchanges['orders-dlx'] * // - contract.queues['order-processing'] * // - contract.bindings.processOrderBinding * // - client.publish('orderCreated', { orderId: string, amount: number }) * // - handler: (message: { orderId: string, amount: number }) => Future> * ``` */ declare function defineContract(definition: TContract): ContractOutput; //#endregion //#region src/builder/routing-types.d.ts /** * Type-safe routing key that validates basic format. * * Validates that a routing key follows basic AMQP routing key rules: * - Must not contain wildcards (* or #) * - Must not be empty * - Should contain alphanumeric characters, dots, hyphens, and underscores * * Note: Full character-by-character validation is not performed to avoid TypeScript * recursion depth limits. Runtime validation is still recommended. * * @public * @template S - The routing key string to validate * @example * ```typescript * type Valid = RoutingKey<"order.created">; // "order.created" * type Invalid = RoutingKey<"order.*">; // never (contains wildcard) * type Invalid2 = RoutingKey<"">; // never (empty string) * ``` */ type RoutingKey = S extends "" ? never : S extends `${string}*${string}` | `${string}#${string}` ? never : S; /** * Type-safe binding pattern that validates basic format and wildcards. * * Validates that a binding pattern follows basic AMQP binding pattern rules: * - Can contain wildcards (* for one word, # for zero or more words) * - Must not be empty * - Should contain alphanumeric characters, dots, hyphens, underscores, and wildcards * * Note: Full character-by-character validation is not performed to avoid TypeScript * recursion depth limits. Runtime validation is still recommended. * * @public * @template S - The binding pattern string to validate * @example * ```typescript * type ValidPattern = BindingPattern<"order.*">; // "order.*" * type ValidHash = BindingPattern<"order.#">; // "order.#" * type ValidConcrete = BindingPattern<"order.created">; // "order.created" * type Invalid = BindingPattern<"">; // never (empty string) * ``` */ type BindingPattern = S extends "" ? never : S; /** * Helper type for pattern matching with # in the middle * Handles backtracking to match # with zero or more segments * @internal */ type MatchesAfterHash = MatchesPattern extends true ? true : Key extends `${string}.${infer KeyRest}` ? MatchesAfterHash : false; /** * Check if a routing key matches a binding pattern * Implements AMQP topic exchange pattern matching: * - * matches exactly one word * - # matches zero or more words * @internal */ type MatchesPattern = Pattern extends `${infer PatternPart}.${infer PatternRest}` ? PatternPart extends "#" ? MatchesAfterHash : Key extends `${infer KeyPart}.${infer KeyRest}` ? PatternPart extends "*" ? MatchesPattern : PatternPart extends KeyPart ? MatchesPattern : false : false : Pattern extends "#" ? true : Pattern extends "*" ? Key extends `${string}.${string}` ? false : true : Pattern extends Key ? true : false; /** * Validate that a routing key matches a binding pattern. * * This is a utility type provided for users who want compile-time validation * that a routing key matches a specific pattern. It's not enforced internally * in the API to avoid TypeScript recursion depth issues with complex routing keys. * * Returns the routing key if it's valid and matches the pattern, `never` otherwise. * * @example * ```typescript * type ValidKey = MatchingRoutingKey<"order.*", "order.created">; // "order.created" * type InvalidKey = MatchingRoutingKey<"order.*", "user.created">; // never * ``` * * @template Pattern - The binding pattern (can contain * and # wildcards) * @template Key - The routing key to validate */ type MatchingRoutingKey = RoutingKey extends never ? never : BindingPattern extends never ? never : MatchesPattern extends true ? Key : never; //#endregion //#region src/builder/event.d.ts /** * Configuration for an event publisher. * * Events are published without knowing who consumes them. Multiple consumers * can subscribe to the same event. This follows the pub/sub pattern where * publishers broadcast events and consumers subscribe to receive them. * * @template TMessage - The message definition * @template TExchange - The exchange definition * @template TRoutingKey - The routing key type (undefined for fanout and headers exchanges) */ type EventPublisherConfig = { /** Discriminator to identify this as an event publisher config */__brand: "EventPublisherConfig"; /** The exchange to publish to */ exchange: TExchange; /** The message definition */ message: TMessage; /** The routing key for direct/topic exchanges */ routingKey: TRoutingKey; /** Additional AMQP arguments */ arguments?: Record; }; /** * Result from defineEventConsumer. * * Contains the consumer definition and binding needed to subscribe to an event. * Can be used directly in defineContract's consumers section - the binding * will be automatically extracted. * * @template TMessage - The message definition */ type EventConsumerResult = { /** Discriminator to identify this as an event consumer result */__brand: "EventConsumerResult"; /** The consumer definition for processing messages */ consumer: ConsumerDefinition; /** The binding connecting the queue to the exchange */ binding: QueueBindingDefinition; /** The source exchange this consumer subscribes to */ exchange: TExchange; /** The queue this consumer reads from */ queue: TQueue; /** The exchange-to-exchange binding when bridging, if configured */ exchangeBinding: TExchangeBinding; /** The bridge (local domain) exchange when bridging, if configured */ bridgeExchange: TBridgeExchange; }; /** * Define an event publisher for broadcasting messages via fanout exchange. * * Events are published without knowing who consumes them. Multiple consumers * can subscribe to the same event using `defineEventConsumer`. * * @param exchange - The fanout exchange to publish to * @param message - The message definition (schema and metadata) * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns An event publisher configuration * * @example * ```typescript * const logsExchange = defineExchange('logs', { type: 'fanout' }); * const logMessage = defineMessage(z.object({ * level: z.enum(['info', 'warn', 'error']), * message: z.string(), * })); * * // Create event publisher * const logEvent = defineEventPublisher(logsExchange, logMessage); * * // Multiple consumers can subscribe * const { consumer: fileConsumer, binding: fileBinding } = * defineEventConsumer(logEvent, fileLogsQueue); * const { consumer: alertConsumer, binding: alertBinding } = * defineEventConsumer(logEvent, alertsQueue); * ``` */ declare function defineEventPublisher(exchange: TExchange, message: TMessage, options?: { arguments?: Record; }): EventPublisherConfig; /** * Define an event publisher for broadcasting messages via headers exchange. * * Events are published without knowing who consumes them. Multiple consumers * can subscribe to the same event using `defineEventConsumer`. * * @param exchange - The headers exchange to publish to * @param message - The message definition (schema and metadata) * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns An event publisher configuration * * @example * ```typescript * const logsExchange = defineExchange('logs', { type: 'headers' }); * const logMessage = defineMessage(z.object({ * level: z.enum(['info', 'warn', 'error']), * message: z.string(), * })); * * // Create event publisher * const logEvent = defineEventPublisher(logsExchange, logMessage); * * // Multiple consumers can subscribe * const { consumer: fileConsumer, binding: fileBinding } = * defineEventConsumer(logEvent, fileLogsQueue); * const { consumer: alertConsumer, binding: alertBinding } = * defineEventConsumer(logEvent, alertsQueue); * ``` */ declare function defineEventPublisher(exchange: TExchange, message: TMessage, options?: { arguments?: Record; }): EventPublisherConfig; /** * Define an event publisher for broadcasting messages via direct exchange. * * Events are published with a specific routing key. Consumers will receive * messages that match the routing key exactly. * * @param exchange - The direct exchange to publish to * @param message - The message definition (schema and metadata) * @param options - Configuration with required routing key * @param options.routingKey - The routing key for message routing * @param options.arguments - Additional AMQP arguments * @returns An event publisher configuration * * @example * ```typescript * const tasksExchange = defineExchange('tasks', { type: 'direct' }); * const taskMessage = defineMessage(z.object({ taskId: z.string() })); * * const taskEvent = defineEventPublisher(tasksExchange, taskMessage, { * routingKey: 'task.execute', * }); * ``` */ declare function defineEventPublisher(exchange: TExchange, message: TMessage, options: { routingKey: RoutingKey; arguments?: Record; }): EventPublisherConfig; /** * Define an event publisher for broadcasting messages via topic exchange. * * Events are published with a concrete routing key. Consumers can subscribe * using patterns (with * and # wildcards) to receive matching messages. * * @param exchange - The topic exchange to publish to * @param message - The message definition (schema and metadata) * @param options - Configuration with required routing key * @param options.routingKey - The concrete routing key (no wildcards) * @param options.arguments - Additional AMQP arguments * @returns An event publisher configuration * * @example * ```typescript * const ordersExchange = defineExchange('orders', { type: 'topic' }); * const orderMessage = defineMessage(z.object({ * orderId: z.string(), * amount: z.number(), * })); * * // Publisher uses concrete routing key * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, { * routingKey: 'order.created', * }); * * // Consumer can use pattern * const { consumer, binding } = defineEventConsumer( * orderCreatedEvent, * allOrdersQueue, * { routingKey: 'order.*' }, * ); * ``` */ declare function defineEventPublisher(exchange: TExchange, message: TMessage, options: { routingKey: RoutingKey; arguments?: Record; }): EventPublisherConfig; /** * Create a consumer that subscribes to an event from a fanout exchange via a bridge exchange. * * When `bridgeExchange` is provided, the queue binds to the bridge exchange instead of the * source exchange, and an exchange-to-exchange binding is created from the source to the bridge. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Binding configuration with required bridgeExchange * @param options.bridgeExchange - The fanout bridge exchange (must be fanout to match source) * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition, queue binding, and exchange binding */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options: { bridgeExchange: TBridgeExchange; arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a headers exchange via a bridge exchange. * * When `bridgeExchange` is provided, the queue binds to the bridge exchange instead of the * source exchange, and an exchange-to-exchange binding is created from the source to the bridge. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Binding configuration with required bridgeExchange * @param options.bridgeExchange - The headers bridge exchange (must be headers to match source) * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition, queue binding, and exchange binding */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options: { bridgeExchange: TBridgeExchange; arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a direct exchange via a bridge exchange. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Binding configuration with required bridgeExchange * @param options.bridgeExchange - The bridge exchange (must be direct or topic to preserve routing keys) * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition, queue binding, and exchange binding */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options: { bridgeExchange: TBridgeExchange; arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a topic exchange via a bridge exchange. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Binding configuration with required bridgeExchange * @param options.bridgeExchange - The bridge exchange (must be direct or topic to preserve routing keys) * @param options.routingKey - Override routing key with pattern (defaults to publisher's key) * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition, queue binding, and exchange binding */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options: { bridgeExchange: TBridgeExchange; routingKey?: BindingPattern; arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a fanout exchange. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition and binding * * @example * ```typescript * const logEvent = defineEventPublisher(logsExchange, logMessage); * const { consumer, binding } = defineEventConsumer(logEvent, logsQueue); * ``` */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options?: { arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a headers exchange. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition and binding * * @example * ```typescript * const logEvent = defineEventPublisher(logsExchange, logMessage); * const { consumer, binding } = defineEventConsumer(logEvent, logsQueue); * ``` */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options?: { arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a direct exchange. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition and binding */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options?: { arguments?: Record; }): EventConsumerResult; /** * Create a consumer that subscribes to an event from a topic exchange. * * For topic exchanges, the consumer can optionally override the routing key * with a pattern to subscribe to multiple events. * * @param eventPublisher - The event publisher configuration * @param queue - The queue that will receive messages * @param options - Optional binding configuration * @param options.routingKey - Override routing key with pattern (defaults to publisher's key) * @param options.arguments - Additional AMQP arguments * @returns An object with the consumer definition and binding * * @example * ```typescript * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, { * routingKey: 'order.created', * }); * * // Use exact routing key from publisher * const { consumer: exactConsumer } = defineEventConsumer(orderCreatedEvent, exactQueue); * * // Override with pattern to receive all order events * const { consumer: allConsumer } = defineEventConsumer(orderCreatedEvent, allQueue, { * routingKey: 'order.*', * }); * ``` */ declare function defineEventConsumer(eventPublisher: EventPublisherConfig, queue: TQueueEntry, options?: { routingKey?: BindingPattern; arguments?: Record; }): EventConsumerResult; /** * Type guard to check if a value is an EventPublisherConfig. * * @param value - The value to check * @returns True if the value is an EventPublisherConfig */ declare function isEventPublisherConfig(value: unknown): value is EventPublisherConfig; /** * Type guard to check if a value is an EventConsumerResult. * * @param value - The value to check * @returns True if the value is an EventConsumerResult */ declare function isEventConsumerResult(value: unknown): value is EventConsumerResult; //#endregion //#region src/builder/command.d.ts /** * Configuration for a command consumer. * * Commands are sent by one or more publishers to a single consumer (task queue pattern). * The consumer "owns" the queue, and publishers send commands to it. * * @template TMessage - The message definition * @template TExchange - The exchange definition * @template TRoutingKey - The routing key type (undefined for fanout and headers exchanges) */ type CommandConsumerConfig = { /** Discriminator to identify this as a command consumer config */__brand: "CommandConsumerConfig"; /** The consumer definition for processing commands */ consumer: ConsumerDefinition; /** The binding connecting the queue to the exchange */ binding: QueueBindingDefinition; /** The exchange that receives commands */ exchange: TExchange; /** The queue this consumer reads from */ queue: TQueue; /** The message definition */ message: TMessage; /** The routing key pattern for the binding */ routingKey: TRoutingKey; }; /** * Configuration for a bridged command publisher. * * A bridged publisher publishes to a bridge exchange (local domain), which forwards * messages to the target exchange (remote domain) via an exchange-to-exchange binding. * * @template TMessage - The message definition * @template TBridgeExchange - The bridge (local domain) exchange definition * @template TTargetExchange - The target (remote domain) exchange definition */ type BridgedPublisherConfig = { /** Discriminator to identify this as a bridged publisher config */__brand: "BridgedPublisherConfig"; /** The publisher definition (publishes to bridge exchange) */ publisher: PublisherDefinition; /** The exchange-to-exchange binding (bridge → target) */ exchangeBinding: ExchangeBindingDefinition; /** The bridge (local domain) exchange */ bridgeExchange: TBridgeExchange; /** The target (remote domain) exchange */ targetExchange: TTargetExchange; }; /** * Define a command consumer for receiving commands via fanout exchange. * * Commands are sent by publishers to a specific queue. The consumer "owns" the * queue and defines what commands it accepts. * * @param queue - The queue that will receive commands * @param exchange - The fanout exchange that routes commands * @param message - The message definition (schema and metadata) * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns A command consumer configuration * * @example * ```typescript * const tasksExchange = defineExchange('tasks', { type: 'fanout' }); * const taskMessage = defineMessage(z.object({ taskId: z.string() })); * * // Consumer owns the queue * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage); * * // Publishers send commands to it * const sendTask = defineCommandPublisher(executeTask); * ``` */ declare function defineCommandConsumer(queue: TQueueEntry, exchange: TExchange, message: TMessage, options?: { arguments?: Record; }): CommandConsumerConfig; /** * Define a command consumer for receiving commands via headers exchange. * * Commands are sent by publishers to a specific queue. The consumer "owns" the * queue and defines what commands it accepts. * * @param queue - The queue that will receive commands * @param exchange - The headers exchange that routes commands * @param message - The message definition (schema and metadata) * @param options - Optional binding configuration * @param options.arguments - Additional AMQP arguments * @returns A command consumer configuration * * @example * ```typescript * const tasksExchange = defineExchange('tasks', { type: 'headers' }); * const taskMessage = defineMessage(z.object({ taskId: z.string() })); * * // Consumer owns the queue * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage); * * // Publishers send commands to it * const sendTask = defineCommandPublisher(executeTask); * ``` */ declare function defineCommandConsumer(queue: TQueueEntry, exchange: TExchange, message: TMessage, options?: { arguments?: Record; }): CommandConsumerConfig; /** * Define a command consumer for receiving commands via direct exchange. * * Commands are sent by publishers with a specific routing key that matches * the binding pattern. * * @param queue - The queue that will receive commands * @param exchange - The direct exchange that routes commands * @param message - The message definition (schema and metadata) * @param options - Configuration with required routing key * @param options.routingKey - The routing key for the binding * @param options.arguments - Additional AMQP arguments * @returns A command consumer configuration * * @example * ```typescript * const tasksExchange = defineExchange('tasks', { type: 'direct' }); * const taskMessage = defineMessage(z.object({ taskId: z.string() })); * * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage, { * routingKey: 'task.execute', * }); * * const sendTask = defineCommandPublisher(executeTask); * ``` */ declare function defineCommandConsumer(queue: TQueueEntry, exchange: TExchange, message: TMessage, options: { routingKey: RoutingKey; arguments?: Record; }): CommandConsumerConfig; /** * Define a command consumer for receiving commands via topic exchange. * * The consumer binds with a routing key pattern (can use * and # wildcards). * Publishers then send commands with concrete routing keys that match the pattern. * * @param queue - The queue that will receive commands * @param exchange - The topic exchange that routes commands * @param message - The message definition (schema and metadata) * @param options - Configuration with required routing key pattern * @param options.routingKey - The routing key pattern for the binding * @param options.arguments - Additional AMQP arguments * @returns A command consumer configuration * * @example * ```typescript * const ordersExchange = defineExchange('orders', { type: 'topic' }); * const orderMessage = defineMessage(z.object({ orderId: z.string() })); * * // Consumer uses pattern to receive multiple command types * const processOrder = defineCommandConsumer(orderQueue, ordersExchange, orderMessage, { * routingKey: 'order.*', * }); * * // Publishers send with concrete keys * const createOrder = defineCommandPublisher(processOrder, { * routingKey: 'order.create', * }); * const updateOrder = defineCommandPublisher(processOrder, { * routingKey: 'order.update', * }); * ``` */ declare function defineCommandConsumer(queue: TQueueEntry, exchange: TExchange, message: TMessage, options: { routingKey: BindingPattern; arguments?: Record; }): CommandConsumerConfig; /** * Create a bridged publisher that sends commands to a fanout exchange consumer via a bridge exchange. * * @param commandConsumer - The command consumer configuration * @param options - Configuration with required bridgeExchange * @param options.bridgeExchange - The local domain exchange to bridge through (must be fanout to match target) * @returns A bridged publisher configuration */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig, options: { bridgeExchange: TBridgeExchange; }): BridgedPublisherConfig; /** * Create a bridged publisher that sends commands to a headers exchange consumer via a bridge exchange. * * @param commandConsumer - The command consumer configuration * @param options - Configuration with required bridgeExchange * @param options.bridgeExchange - The local domain exchange to bridge through (must be headers to match target) * @returns A bridged publisher configuration */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig, options: { bridgeExchange: TBridgeExchange; }): BridgedPublisherConfig; /** * Create a bridged publisher that sends commands to a direct exchange consumer via a bridge exchange. * * @param commandConsumer - The command consumer configuration * @param options - Configuration with required bridgeExchange * @param options.bridgeExchange - The bridge exchange (must be direct or topic to preserve routing keys) * @returns A bridged publisher configuration */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig, options: { bridgeExchange: TBridgeExchange; }): BridgedPublisherConfig; /** * Create a bridged publisher that sends commands to a topic exchange consumer via a bridge exchange. * * @param commandConsumer - The command consumer configuration * @param options - Configuration with required bridgeExchange and optional routingKey override * @param options.bridgeExchange - The bridge exchange (must be direct or topic to preserve routing keys) * @param options.routingKey - Override routing key (must match consumer's pattern) * @returns A bridged publisher configuration */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig, options: { bridgeExchange: TBridgeExchange; routingKey?: RoutingKey; }): BridgedPublisherConfig; /** * Create a publisher that sends commands to a fanout exchange consumer. * * @param commandConsumer - The command consumer configuration * @returns A publisher definition * * @example * ```typescript * const executeTask = defineCommandConsumer(taskQueue, fanoutExchange, taskMessage); * const sendTask = defineCommandPublisher(executeTask); * ``` */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig): { message: TMessage; exchange: FanoutExchangeDefinition; }; /** * Create a publisher that sends commands to a headers exchange consumer. * * @param commandConsumer - The command consumer configuration * @returns A publisher definition * * @example * ```typescript * const executeTask = defineCommandConsumer(taskQueue, headersExchange, taskMessage); * const sendTask = defineCommandPublisher(executeTask); * ``` */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig): { message: TMessage; exchange: HeadersExchangeDefinition; }; /** * Create a publisher that sends commands to a direct exchange consumer. * * @param commandConsumer - The command consumer configuration * @returns A publisher definition */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig): { message: TMessage; exchange: DirectExchangeDefinition; routingKey: string; }; /** * Create a publisher that sends commands to a topic exchange consumer. * * For topic exchanges where the consumer uses a pattern, the publisher can * optionally specify a concrete routing key that matches the pattern. * * @param commandConsumer - The command consumer configuration * @param options - Optional binding configuration * @param options.routingKey - Override routing key (must match consumer's pattern) * @returns A publisher definition * * @example * ```typescript * // Consumer binds with pattern * const processOrder = defineCommandConsumer(orderQueue, topicExchange, orderMessage, { * routingKey: 'order.*', * }); * * // Publisher uses concrete key matching the pattern * const createOrder = defineCommandPublisher(processOrder, { * routingKey: 'order.create', * }); * ``` */ declare function defineCommandPublisher(commandConsumer: CommandConsumerConfig, options?: { routingKey?: RoutingKey; }): { message: TMessage; exchange: TopicExchangeDefinition; routingKey: string; }; /** * Type guard to check if a value is a CommandConsumerConfig. * * @param value - The value to check * @returns True if the value is a CommandConsumerConfig */ declare function isCommandConsumerConfig(value: unknown): value is CommandConsumerConfig; /** * Type guard to check if a value is a BridgedPublisherConfig. * * @param value - The value to check * @returns True if the value is a BridgedPublisherConfig */ declare function isBridgedPublisherConfig(value: unknown): value is BridgedPublisherConfig; //#endregion //#region src/builder/rpc.d.ts /** * Define an RPC operation: a request/response pair flowing over a request * queue with replies routed back via RabbitMQ direct reply-to. * * RPC is bidirectional on both ends — the worker handler consumes the request * and produces the response; `client.call(name, request, options)` publishes * the request and awaits the typed response. Both sides share the same * definition, so request and response schemas cannot drift between them. * * Plug the result into `defineContract({ rpcs: { name: ... } })`. RPCs do not * appear in `publishers` or `consumers`. * * @param queue - The queue that receives RPC requests. The queue name is * used as the routing key on the AMQP default direct exchange. * @param messages.request - Schema validated against incoming request payloads * (server side) and outgoing requests (client side). * @param messages.response - Schema validated against handler return values * (server side) and incoming replies (client side). * @param messages.errors - Optional typed error map: error code → message * definition for the error's `data` payload. Declared errors widen the * handler's `Err` channel (return `Err(rpcError(code, data))`) and the * client's `call()` error union; error data is schema-validated on both * sides. Business errors are replied and acked — never retried. * * @example * ```typescript * import { defineQueue, defineMessage, defineRpc, defineContract } from '@amqp-contract/contract'; * import { z } from 'zod'; * * const getOrder = defineRpc(defineQueue('rpc.get-order'), { * request: defineMessage(z.object({ orderId: z.string() })), * response: defineMessage(z.object({ orderId: z.string(), status: z.string() })), * errors: { * ORDER_NOT_FOUND: defineMessage(z.object({ orderId: z.string() })), * }, * }); * * const contract = defineContract({ rpcs: { getOrder } }); * * // Server (worker): return the response, or a declared typed error * // handlers: { * // getOrder: ({ payload }) => * // orders.has(payload.orderId) * // ? OkAsync(orders.get(payload.orderId)) * // : ErrAsync(rpcError('ORDER_NOT_FOUND', { orderId: payload.orderId })), * // } * * // Client: typed call — the error union includes RpcError<'ORDER_NOT_FOUND', { orderId: string }> * // const result = await client.call('getOrder', { orderId: '42' }, { timeoutMs: 5_000 }); * // if (result.isErr() && isRpcError(result.error)) console.log(result.error.code); * ``` */ declare function defineRpc(queue: TQueue, messages: { request: TRequestMessage; response: TResponseMessage; errors?: TErrors; }): RpcDefinition; //#endregion //#region src/builder/ttl-backoff.d.ts /** * Type guard to check if a queue entry is a QueueWithTtlBackoffInfrastructure. * * When you configure a queue with TTL-backoff retry, * `defineQueue` returns a `QueueWithTtlBackoffInfrastructure` instead of a plain * `QueueDefinition`. This type guard helps you distinguish between the two. * * **When to use:** * - When you need to check the type of a queue entry at runtime * - When writing generic code that handles both plain queues and infrastructure wrappers * * **Related functions:** * - `extractQueue()` - Use this to get the underlying queue definition from either type * * @param entry - The queue entry to check * @returns True if the entry is a QueueWithTtlBackoffInfrastructure, false otherwise * * @example * ```typescript * const queue = defineQueue('orders', { * retry: { mode: 'ttl-backoff' }, * }); * * if (isQueueWithTtlBackoffInfrastructure(queue)) { * // queue has .queue, .waitQueue, .waitQueueBinding, .retryQueueBinding, .waitExchange, .retryExchange * console.log('Wait queue:', queue.waitQueue.name); * } else { * // queue is a plain QueueDefinition * console.log('Queue:', queue.name); * } * ``` */ declare function isQueueWithTtlBackoffInfrastructure(entry: QueueEntry): entry is QueueWithTtlBackoffInfrastructure; //#endregion //#region src/issues.d.ts /** * Render a single Standard Schema issue as `path.to.field: message` (or just * the message for root-level issues). Path segments may be raw property keys * or `{ key }` objects per the Standard Schema spec; both are handled. * * Single source of truth for issue rendering across the client and worker — * mirrors temporal-contract's shared formatter (org DNA). */ declare function formatIssue(issue: StandardSchemaV1.Issue): string; /** * Render a list of Standard Schema issues as a single human-readable line: * the first `limit` issues joined with `; `, plus a `(+N more)` suffix when * truncated. Empty input renders as `"no issues"` (defensive — validation * failures always carry at least one issue). */ declare function summarizeIssues(issues: readonly StandardSchemaV1.Issue[], limit?: number): string; //#endregion export { type AnySchema, type BaseExchangeDefinition, type BindingDefinition, type BindingPattern, type BridgedPublisherConfig, type BridgedPublisherConfigBase, type ClassicQueueDefinition, type ClassicQueueOptions, type CommandConsumerConfig, type CommandConsumerConfigBase, type CompressionAlgorithm, type ConsumerDefinition, type ConsumerEntry, type ContractDefinition, type ContractDefinitionInput, type ContractOutput, type DeadLetterConfig, type DefineQueueOptions, type DirectExchangeDefinition, type EventConsumerResult, type EventConsumerResultBase, type EventPublisherConfig, type EventPublisherConfigBase, type ExchangeBindingDefinition, type ExchangeDefinition, type FanoutExchangeDefinition, type HeadersExchangeDefinition, type ImmediateRequeueRetryOptions, type InferConsumerNames, type InferPublisherNames, type InferRpcNames, type MatchingRoutingKey, type MessageDefinition, type PublisherDefinition, type PublisherEntry, type QueueBindingDefinition, type QueueDefinition, type QueueEntry, type QueueType, type QueueWithTtlBackoffInfrastructure, type QuorumQueueDefinition, type QuorumQueueOptions, type ResolvedImmediateRequeueRetryOptions, type ResolvedRetryOptions, type ResolvedTtlBackoffRetryOptions, type RoutingKey, type RpcDefinition, type RpcErrorMap, type TopicExchangeDefinition, type TtlBackoffRetryOptions, defineCommandConsumer, defineCommandPublisher, defineConsumer, defineContract, defineEventConsumer, defineEventPublisher, defineExchange, defineExchangeBinding, defineMessage, definePublisher, defineQueue, defineQueueBinding, defineRpc, extractConsumer, extractQueue, formatIssue, isBridgedPublisherConfig, isCommandConsumerConfig, isEventConsumerResult, isEventPublisherConfig, isQueueWithTtlBackoffInfrastructure, summarizeIssues }; //# sourceMappingURL=index.d.cts.map