@amqp-contract/contract 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -13
- package/dist/index.cjs +47 -0
- package/dist/index.d.cts +37 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +37 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +47 -1
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +211 -162
- package/package.json +1 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["isQueueWithTtlBackoffInfrastructure","retry","queueDefinition"],"sources":["../src/builder/exchange.ts","../src/builder/message.ts","../src/builder/binding.ts","../src/builder/queue.ts","../src/builder/publisher.ts","../src/builder/consumer.ts","../src/builder/event.ts","../src/builder/command.ts","../src/builder/contract.ts","../src/builder/ttl-backoff.ts"],"sourcesContent":["import type {\n BaseExchangeDefinition,\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n TopicExchangeDefinition,\n} from \"../types.js\";\n\n/**\n * Define a fanout exchange.\n *\n * A fanout exchange routes messages to all bound queues without considering routing keys.\n * This exchange type is ideal for broadcasting messages to multiple consumers.\n *\n * @param name - The name of the exchange\n * @param type - Must be \"fanout\"\n * @param options - Optional exchange configuration\n * @param options.durable - If true, the exchange survives broker restarts (default: false)\n * @param options.autoDelete - If true, the exchange is deleted when no queues are bound (default: false)\n * @param options.internal - If true, the exchange cannot be directly published to (default: false)\n * @param options.arguments - Additional AMQP arguments for the exchange\n * @returns A fanout exchange definition\n *\n * @example\n * ```typescript\n * const logsExchange = defineExchange('logs', 'fanout', {\n * durable: true\n * });\n * ```\n */\nexport function defineExchange(\n name: string,\n type: \"fanout\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): FanoutExchangeDefinition;\n\n/**\n * Define a direct exchange.\n *\n * A direct exchange routes messages to queues based on exact routing key matches.\n * This exchange type is ideal for point-to-point messaging.\n *\n * @param name - The name of the exchange\n * @param type - Must be \"direct\"\n * @param options - Optional exchange configuration\n * @param options.durable - If true, the exchange survives broker restarts (default: false)\n * @param options.autoDelete - If true, the exchange is deleted when no queues are bound (default: false)\n * @param options.internal - If true, the exchange cannot be directly published to (default: false)\n * @param options.arguments - Additional AMQP arguments for the exchange\n * @returns A direct exchange definition\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'direct', {\n * durable: true\n * });\n * ```\n */\nexport function defineExchange(\n name: string,\n type: \"direct\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): DirectExchangeDefinition;\n\n/**\n * Define a topic exchange.\n *\n * A topic exchange routes messages to queues based on routing key patterns.\n * Routing keys can use wildcards: `*` matches one word, `#` matches zero or more words.\n * This exchange type is ideal for flexible message routing based on hierarchical topics.\n *\n * @param name - The name of the exchange\n * @param type - Must be \"topic\"\n * @param options - Optional exchange configuration\n * @param options.durable - If true, the exchange survives broker restarts (default: false)\n * @param options.autoDelete - If true, the exchange is deleted when no queues are bound (default: false)\n * @param options.internal - If true, the exchange cannot be directly published to (default: false)\n * @param options.arguments - Additional AMQP arguments for the exchange\n * @returns A topic exchange definition\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', {\n * durable: true\n * });\n * ```\n */\nexport function defineExchange(\n name: string,\n type: \"topic\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): TopicExchangeDefinition;\n\n/**\n * Define an AMQP exchange.\n *\n * An exchange receives messages from publishers and routes them to queues based on the exchange type\n * and routing rules. This is the implementation function - use the type-specific overloads for better\n * type safety.\n *\n * @param name - The name of the exchange\n * @param type - The type of exchange: \"fanout\", \"direct\", or \"topic\"\n * @param options - Optional exchange configuration\n * @returns An exchange definition\n * @internal\n */\nexport function defineExchange(\n name: string,\n type: \"fanout\" | \"direct\" | \"topic\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): ExchangeDefinition {\n return {\n name,\n type,\n ...options,\n };\n}\n","import type { MessageDefinition } from \"../types.js\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Define a message definition with payload and optional headers/metadata.\n *\n * A message definition specifies the schema for message payloads and headers using\n * Standard Schema v1 compatible libraries (Zod, Valibot, ArkType, etc.).\n * The schemas are used for automatic validation when publishing or consuming messages.\n *\n * @param payload - The payload schema (must be Standard Schema v1 compatible)\n * @param options - Optional message metadata\n * @param options.headers - Optional header schema for message headers\n * @param options.summary - Brief description for documentation (used in AsyncAPI generation)\n * @param options.description - Detailed description for documentation (used in AsyncAPI generation)\n * @returns A message definition with inferred types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string().uuid(),\n * customerId: z.string().uuid(),\n * amount: z.number().positive(),\n * items: z.array(z.object({\n * productId: z.string(),\n * quantity: z.number().int().positive(),\n * })),\n * }),\n * {\n * summary: 'Order created event',\n * description: 'Emitted when a new order is created in the system'\n * }\n * );\n * ```\n */\nexport function defineMessage<\n TPayload extends MessageDefinition[\"payload\"],\n THeaders extends StandardSchemaV1<Record<string, unknown>> | undefined = undefined,\n>(\n payload: TPayload,\n options?: {\n headers?: THeaders;\n summary?: string;\n description?: string;\n },\n): MessageDefinition<TPayload, THeaders> {\n return {\n payload,\n ...options,\n };\n}\n","import type {\n DirectExchangeDefinition,\n ExchangeBindingDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n QueueBindingDefinition,\n QueueDefinition,\n QueueEntry,\n QueueWithTtlBackoffInfrastructure,\n TopicExchangeDefinition,\n} from \"../types.js\";\n\n/**\n * Type guard to check if a queue entry is a QueueWithTtlBackoffInfrastructure.\n * Duplicated here to avoid circular dependency with queue.ts.\n * @internal\n */\nfunction isQueueWithTtlBackoffInfrastructure(\n entry: QueueEntry,\n): entry is QueueWithTtlBackoffInfrastructure {\n return (\n typeof entry === \"object\" &&\n entry !== null &&\n \"__brand\" in entry &&\n entry.__brand === \"QueueWithTtlBackoffInfrastructure\"\n );\n}\n\n/**\n * Extract the plain QueueDefinition from a QueueEntry.\n * Duplicated here to avoid circular dependency with queue.ts.\n * @internal\n */\nfunction extractQueueInternal(entry: QueueEntry): QueueDefinition {\n if (isQueueWithTtlBackoffInfrastructure(entry)) {\n return entry.queue;\n }\n return entry;\n}\n\n/**\n * Define a binding between a queue and a fanout exchange.\n *\n * Binds a queue to a fanout exchange to receive all messages published to the exchange.\n * Fanout exchanges ignore routing keys, so this overload doesn't require one.\n *\n * @param queue - The queue definition or queue with infrastructure to bind\n * @param exchange - The fanout exchange definition\n * @param options - Optional binding configuration\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns A queue binding definition\n *\n * @example\n * ```typescript\n * const logsQueue = defineQueue('logs-queue', { durable: true });\n * const logsExchange = defineExchange('logs', 'fanout', { durable: true });\n *\n * const binding = defineQueueBinding(logsQueue, logsExchange);\n * ```\n */\nexport function defineQueueBinding(\n queue: QueueEntry,\n exchange: FanoutExchangeDefinition,\n options?: Omit<\n Extract<QueueBindingDefinition, { exchange: FanoutExchangeDefinition }>,\n \"type\" | \"queue\" | \"exchange\" | \"routingKey\"\n >,\n): Extract<QueueBindingDefinition, { exchange: FanoutExchangeDefinition }>;\n\n/**\n * Define a binding between a queue and a direct or topic exchange.\n *\n * Binds a queue to an exchange with a specific routing key pattern.\n * Messages are only routed to the queue if the routing key matches the pattern.\n *\n * For direct exchanges: The routing key must match exactly.\n * For topic exchanges: The routing key can include wildcards:\n * - `*` matches exactly one word\n * - `#` matches zero or more words\n *\n * @param queue - The queue definition or queue with infrastructure to bind\n * @param exchange - The direct or topic exchange definition\n * @param options - Binding configuration (routingKey is required)\n * @param options.routingKey - The routing key pattern for message routing\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns A queue binding definition\n *\n * @example\n * ```typescript\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n *\n * // Bind with exact routing key\n * const binding = defineQueueBinding(orderQueue, ordersExchange, {\n * routingKey: 'order.created'\n * });\n *\n * // Bind with wildcard pattern\n * const allOrdersBinding = defineQueueBinding(orderQueue, ordersExchange, {\n * routingKey: 'order.*' // Matches order.created, order.updated, etc.\n * });\n * ```\n */\nexport function defineQueueBinding(\n queue: QueueEntry,\n exchange: DirectExchangeDefinition | TopicExchangeDefinition,\n options: Omit<\n Extract<\n QueueBindingDefinition,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n >,\n \"type\" | \"queue\" | \"exchange\"\n >,\n): Extract<\n QueueBindingDefinition,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n>;\n\n/**\n * Define a binding between a queue and an exchange.\n *\n * This is the implementation function - use the type-specific overloads for better type safety.\n *\n * @param queue - The queue definition or queue with infrastructure to bind\n * @param exchange - The exchange definition\n * @param options - Optional binding configuration\n * @returns A queue binding definition\n * @internal\n */\nexport function defineQueueBinding(\n queue: QueueEntry,\n exchange: ExchangeDefinition,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): QueueBindingDefinition {\n // Extract the plain queue definition from QueueEntry\n const queueDef = extractQueueInternal(queue);\n\n if (exchange.type === \"fanout\") {\n return {\n type: \"queue\",\n queue: queueDef,\n exchange,\n ...(options?.arguments && { arguments: options.arguments }),\n } as QueueBindingDefinition;\n }\n\n return {\n type: \"queue\",\n queue: queueDef,\n exchange,\n routingKey: options?.routingKey,\n ...(options?.arguments && { arguments: options.arguments }),\n } as QueueBindingDefinition;\n}\n\n/**\n * Internal helper to call defineQueueBinding with proper type handling.\n * Used by queue.ts to avoid circular dependency.\n * @internal\n */\nexport function defineQueueBindingInternal(\n queue: QueueEntry,\n exchange: ExchangeDefinition,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): QueueBindingDefinition {\n if (exchange.type === \"fanout\") {\n return defineQueueBinding(queue, exchange, options);\n }\n return defineQueueBinding(queue, exchange, options as { routingKey: string });\n}\n\n/**\n * Define a binding between two exchanges (exchange-to-exchange routing).\n *\n * Binds a destination exchange to a fanout source exchange.\n * Messages published to the source exchange will be forwarded to the destination exchange.\n * Fanout exchanges ignore routing keys, so this overload doesn't require one.\n *\n * @param destination - The destination exchange definition\n * @param source - The fanout source exchange definition\n * @param options - Optional binding configuration\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns An exchange binding definition\n *\n * @example\n * ```typescript\n * const sourceExchange = defineExchange('logs', 'fanout', { durable: true });\n * const destExchange = defineExchange('all-logs', 'fanout', { durable: true });\n *\n * const binding = defineExchangeBinding(destExchange, sourceExchange);\n * ```\n */\nexport function defineExchangeBinding(\n destination: ExchangeDefinition,\n source: FanoutExchangeDefinition,\n options?: Omit<\n Extract<ExchangeBindingDefinition, { source: FanoutExchangeDefinition }>,\n \"type\" | \"source\" | \"destination\" | \"routingKey\"\n >,\n): Extract<ExchangeBindingDefinition, { source: FanoutExchangeDefinition }>;\n\n/**\n * Define a binding between two exchanges (exchange-to-exchange routing).\n *\n * Binds a destination exchange to a direct or topic source exchange with a routing key pattern.\n * Messages are forwarded from source to destination only if the routing key matches the pattern.\n *\n * @param destination - The destination exchange definition\n * @param source - The direct or topic source exchange definition\n * @param options - Binding configuration (routingKey is required)\n * @param options.routingKey - The routing key pattern for message routing\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns An exchange binding definition\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const importantExchange = defineExchange('important-orders', 'topic', { durable: true });\n *\n * // Forward only high-value orders\n * const binding = defineExchangeBinding(importantExchange, ordersExchange, {\n * routingKey: 'order.high-value.*'\n * });\n * ```\n */\nexport function defineExchangeBinding(\n destination: ExchangeDefinition,\n source: DirectExchangeDefinition | TopicExchangeDefinition,\n options: Omit<\n Extract<\n ExchangeBindingDefinition,\n { source: DirectExchangeDefinition | TopicExchangeDefinition }\n >,\n \"type\" | \"source\" | \"destination\"\n >,\n): Extract<\n ExchangeBindingDefinition,\n { source: DirectExchangeDefinition | TopicExchangeDefinition }\n>;\n\n/**\n * Define a binding between two exchanges (exchange-to-exchange routing).\n *\n * This is the implementation function - use the type-specific overloads for better type safety.\n *\n * @param destination - The destination exchange definition\n * @param source - The source exchange definition\n * @param options - Optional binding configuration\n * @returns An exchange binding definition\n * @internal\n */\nexport function defineExchangeBinding(\n destination: ExchangeDefinition,\n source: ExchangeDefinition,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): ExchangeBindingDefinition {\n if (source.type === \"fanout\") {\n return {\n type: \"exchange\",\n source,\n destination,\n ...(options?.arguments && { arguments: options.arguments }),\n } as ExchangeBindingDefinition;\n }\n\n return {\n type: \"exchange\",\n source,\n destination,\n routingKey: options?.routingKey ?? \"\",\n ...(options?.arguments && { arguments: options.arguments }),\n } as ExchangeBindingDefinition;\n}\n","import type {\n ClassicQueueDefinition,\n ClassicQueueOptions,\n DeadLetterConfig,\n DefineQueueOptions,\n ExchangeDefinition,\n QueueDefinition,\n QueueEntry,\n QueueWithTtlBackoffInfrastructure,\n QuorumQueueDefinition,\n QuorumQueueOptions,\n ResolvedTtlBackoffRetryOptions,\n TtlBackoffRetryOptions,\n} from \"../types.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Resolve TTL-backoff retry options with defaults applied.\n * @internal\n */\nexport function resolveTtlBackoffOptions(\n options: TtlBackoffRetryOptions | undefined,\n): ResolvedTtlBackoffRetryOptions {\n return {\n mode: \"ttl-backoff\",\n maxRetries: options?.maxRetries ?? 3,\n initialDelayMs: options?.initialDelayMs ?? 1000,\n maxDelayMs: options?.maxDelayMs ?? 30000,\n backoffMultiplier: options?.backoffMultiplier ?? 2,\n jitter: options?.jitter ?? true,\n };\n}\n\n/**\n * Type guard to check if a queue entry is a QueueWithTtlBackoffInfrastructure.\n *\n * When you configure a queue with TTL-backoff retry and a dead letter exchange,\n * `defineQueue` returns a `QueueWithTtlBackoffInfrastructure` instead of a plain\n * `QueueDefinition`. This type guard helps you distinguish between the two.\n *\n * **When to use:**\n * - When you need to check the type of a queue entry at runtime\n * - When writing generic code that handles both plain queues and infrastructure wrappers\n *\n * **Related functions:**\n * - `extractQueue()` - Use this to get the underlying queue definition from either type\n *\n * @param entry - The queue entry to check\n * @returns True if the entry is a QueueWithTtlBackoffInfrastructure, false otherwise\n *\n * @example\n * ```typescript\n * const queue = defineQueue('orders', {\n * deadLetter: { exchange: dlx },\n * retry: { mode: 'ttl-backoff' },\n * });\n *\n * if (isQueueWithTtlBackoffInfrastructure(queue)) {\n * // queue has .queue, .waitQueue, .waitQueueBinding, .mainQueueRetryBinding\n * console.log('Wait queue:', queue.waitQueue.name);\n * } else {\n * // queue is a plain QueueDefinition\n * console.log('Queue:', queue.name);\n * }\n * ```\n */\nexport function isQueueWithTtlBackoffInfrastructure(\n entry: QueueEntry,\n): entry is QueueWithTtlBackoffInfrastructure {\n return (\n typeof entry === \"object\" &&\n entry !== null &&\n \"__brand\" in entry &&\n entry.__brand === \"QueueWithTtlBackoffInfrastructure\"\n );\n}\n\n/**\n * Extract the plain QueueDefinition from a QueueEntry.\n *\n * **Why this function exists:**\n * When you configure a queue with TTL-backoff retry and a dead letter exchange,\n * `defineQueue` (or `defineTtlBackoffQueue`) returns a wrapper object that includes\n * the main queue, wait queue, and bindings. This function extracts the underlying\n * queue definition so you can access properties like `name`, `type`, etc.\n *\n * **When to use:**\n * - When you need to access queue properties (name, type, deadLetter, etc.)\n * - When passing a queue to functions that expect a plain QueueDefinition\n * - Works safely on both plain queues and infrastructure wrappers\n *\n * **How it works:**\n * - If the entry is a `QueueWithTtlBackoffInfrastructure`, returns `entry.queue`\n * - Otherwise, returns the entry as-is (it's already a plain QueueDefinition)\n *\n * @param entry - The queue entry (either plain QueueDefinition or QueueWithTtlBackoffInfrastructure)\n * @returns The plain QueueDefinition\n *\n * @example\n * ```typescript\n * import { defineQueue, defineTtlBackoffQueue, extractQueue } from '@amqp-contract/contract';\n *\n * // TTL-backoff queue returns a wrapper\n * const orderQueue = defineTtlBackoffQueue('orders', {\n * deadLetterExchange: dlx,\n * maxRetries: 3,\n * });\n *\n * // Use extractQueue to access the queue name\n * const queueName = extractQueue(orderQueue).name; // 'orders'\n *\n * // Also works safely on plain queues\n * const plainQueue = defineQueue('simple', { type: 'quorum', retry: { mode: 'quorum-native' } });\n * const plainName = extractQueue(plainQueue).name; // 'simple'\n *\n * // Access other properties\n * const queueDef = extractQueue(orderQueue);\n * console.log(queueDef.name); // 'orders'\n * console.log(queueDef.type); // 'quorum'\n * console.log(queueDef.deadLetter); // { exchange: dlx, ... }\n * ```\n *\n * @see isQueueWithTtlBackoffInfrastructure - Type guard to check if extraction is needed\n * @see defineTtlBackoffQueue - Creates queues with TTL-backoff infrastructure\n */\nexport function extractQueue(entry: QueueEntry): QueueDefinition {\n if (isQueueWithTtlBackoffInfrastructure(entry)) {\n return entry.queue;\n }\n return entry;\n}\n\n/**\n * Wrap a queue definition with TTL-backoff retry infrastructure.\n * @internal\n */\nfunction wrapWithTtlBackoffInfrastructure(\n queue: QueueDefinition,\n): QueueWithTtlBackoffInfrastructure {\n if (!queue.deadLetter) {\n throw new Error(\n `Queue \"${queue.name}\" does not have a dead letter exchange configured. ` +\n `TTL-backoff retry requires deadLetter to be set on the queue.`,\n );\n }\n\n const dlx = queue.deadLetter.exchange;\n const waitQueueName = `${queue.name}-wait`;\n\n // Create the wait queue - quorum for better durability\n // Wait queue uses TTL-backoff mode (infrastructure queue, not directly consumed)\n const waitQueue: QuorumQueueDefinition = {\n name: waitQueueName,\n type: \"quorum\",\n durable: queue.durable ?? true,\n deadLetter: {\n exchange: dlx,\n routingKey: queue.name, // Routes back to main queue after TTL\n },\n retry: resolveTtlBackoffOptions(undefined),\n };\n\n // Create binding for wait queue to receive failed messages\n const waitQueueBinding = defineQueueBindingInternal(waitQueue, dlx, {\n routingKey: waitQueueName,\n });\n\n // Create binding for main queue to receive retried messages\n const mainQueueRetryBinding = defineQueueBindingInternal(queue, dlx, {\n routingKey: queue.name,\n });\n\n return {\n __brand: \"QueueWithTtlBackoffInfrastructure\",\n queue,\n waitQueue,\n waitQueueBinding,\n mainQueueRetryBinding,\n };\n}\n\n/**\n * Define an AMQP queue.\n *\n * A queue stores messages until they are consumed by workers. Queues can be bound to exchanges\n * to receive messages based on routing rules.\n *\n * By default, queues are created as quorum queues which provide better durability and\n * high-availability. Use `type: 'classic'` for special cases like non-durable queues\n * or priority queues.\n *\n * @param name - The name of the queue\n * @param options - Optional queue configuration\n * @param options.type - Queue type: 'quorum' (default, recommended) or 'classic'\n * @param options.durable - If true, the queue survives broker restarts. Quorum queues are always durable.\n * @param options.exclusive - If true, the queue can only be used by the declaring connection. Only supported with classic queues.\n * @param options.autoDelete - If true, the queue is deleted when the last consumer unsubscribes (default: false)\n * @param options.deadLetter - Dead letter configuration for handling failed messages\n * @param options.maxPriority - Maximum priority level for priority queue (1-255, recommended: 1-10). Only supported with classic queues.\n * @param options.arguments - Additional AMQP arguments (e.g., x-message-ttl)\n * @returns A queue definition\n *\n * @example\n * ```typescript\n * // Quorum queue (default, recommended for production)\n * const orderQueue = defineQueue('order-processing');\n *\n * // Explicit quorum queue with dead letter exchange\n * const dlx = defineExchange('orders-dlx', 'topic', { durable: true });\n * const orderQueueWithDLX = defineQueue('order-processing', {\n * type: 'quorum',\n * deadLetter: {\n * exchange: dlx,\n * routingKey: 'order.failed'\n * },\n * arguments: {\n * 'x-message-ttl': 86400000, // 24 hours\n * }\n * });\n *\n * // Classic queue (for special cases)\n * const tempQueue = defineQueue('temp-queue', {\n * type: 'classic',\n * durable: false,\n * autoDelete: true,\n * });\n *\n * // Priority queue (requires classic type)\n * const taskQueue = defineQueue('urgent-tasks', {\n * type: 'classic',\n * durable: true,\n * maxPriority: 10,\n * });\n *\n * // Queue with TTL-backoff retry (returns infrastructure automatically)\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n * const orderQueue = defineQueue('order-processing', {\n * deadLetter: { exchange: dlx },\n * retry: { mode: 'ttl-backoff', maxRetries: 5 },\n * });\n * // orderQueue is QueueWithTtlBackoffInfrastructure, pass directly to defineContract\n * ```\n */\nexport function defineQueue(\n name: string,\n options?: DefineQueueOptions,\n): QueueDefinition | QueueWithTtlBackoffInfrastructure {\n const opts = options ?? {};\n const type = opts.type ?? \"quorum\";\n\n // Build base properties shared by both queue types\n const baseProps: {\n name: string;\n durable?: boolean;\n autoDelete?: boolean;\n deadLetter?: DeadLetterConfig;\n arguments?: Record<string, unknown>;\n } = { name };\n\n if (opts.durable !== undefined) {\n baseProps.durable = opts.durable;\n }\n\n if (opts.autoDelete !== undefined) {\n baseProps.autoDelete = opts.autoDelete;\n }\n\n if (opts.deadLetter !== undefined) {\n baseProps.deadLetter = opts.deadLetter;\n }\n\n if (opts.arguments !== undefined) {\n baseProps.arguments = opts.arguments;\n }\n\n // Build quorum queue\n if (type === \"quorum\") {\n const quorumOpts = opts as QuorumQueueOptions;\n const inputRetry = quorumOpts.retry ?? { mode: \"ttl-backoff\" as const };\n\n // Validate quorum-native retry requirements\n if (inputRetry.mode === \"quorum-native\") {\n if (quorumOpts.deliveryLimit === undefined) {\n throw new Error(\n `Queue \"${name}\" uses quorum-native retry mode but deliveryLimit is not configured. ` +\n `Quorum-native retry requires deliveryLimit to be set.`,\n );\n }\n }\n\n // Resolve retry options: apply defaults for TTL-backoff, keep quorum-native as-is\n const retry =\n inputRetry.mode === \"quorum-native\" ? inputRetry : resolveTtlBackoffOptions(inputRetry);\n\n const queueDefinition: QuorumQueueDefinition = {\n ...baseProps,\n type: \"quorum\",\n retry,\n };\n\n // Validate and add deliveryLimit\n if (quorumOpts.deliveryLimit !== undefined) {\n if (quorumOpts.deliveryLimit < 1 || !Number.isInteger(quorumOpts.deliveryLimit)) {\n throw new Error(\n `Invalid deliveryLimit: ${quorumOpts.deliveryLimit}. Must be a positive integer.`,\n );\n }\n queueDefinition.deliveryLimit = quorumOpts.deliveryLimit;\n }\n\n // If TTL-backoff retry with dead letter exchange, wrap with infrastructure\n if (retry.mode === \"ttl-backoff\" && queueDefinition.deadLetter) {\n return wrapWithTtlBackoffInfrastructure(queueDefinition);\n }\n\n return queueDefinition;\n }\n\n // Build classic queue\n const classicOpts = opts as ClassicQueueOptions;\n\n // Classic queues cannot use quorum-native retry mode\n if ((classicOpts.retry as { mode?: string } | undefined)?.mode === \"quorum-native\") {\n throw new Error(\n `Queue \"${name}\" uses quorum-native retry mode but is a classic queue. ` +\n `Quorum-native retry requires quorum queues (type: \"quorum\").`,\n );\n }\n\n // Resolve TTL-backoff options with defaults\n const retry = resolveTtlBackoffOptions(classicOpts.retry);\n\n const queueDefinition: ClassicQueueDefinition = {\n ...baseProps,\n type: \"classic\",\n retry,\n };\n\n // Add exclusive\n if (classicOpts.exclusive !== undefined) {\n queueDefinition.exclusive = classicOpts.exclusive;\n }\n\n // Validate and add maxPriority argument\n if (classicOpts.maxPriority !== undefined) {\n if (classicOpts.maxPriority < 1 || classicOpts.maxPriority > 255) {\n throw new Error(\n `Invalid maxPriority: ${classicOpts.maxPriority}. Must be between 1 and 255. Recommended range: 1-10.`,\n );\n }\n queueDefinition.arguments = {\n ...queueDefinition.arguments,\n \"x-max-priority\": classicOpts.maxPriority,\n };\n }\n\n // If TTL-backoff retry with dead letter exchange, wrap with infrastructure\n if (retry.mode === \"ttl-backoff\" && queueDefinition.deadLetter) {\n return wrapWithTtlBackoffInfrastructure(queueDefinition);\n }\n\n return queueDefinition;\n}\n\n// =============================================================================\n// Simplified Queue Configuration Helpers\n// =============================================================================\n\n/**\n * Options for creating a quorum queue with quorum-native retry.\n *\n * This simplified helper enforces the required configuration for quorum-native retry:\n * - Dead letter exchange is required (for failed messages)\n * - Delivery limit is required (for retry count)\n */\nexport type DefineQuorumQueueOptions = {\n /**\n * Dead letter configuration - required for retry support.\n * Failed messages will be sent to this exchange.\n */\n deadLetterExchange: ExchangeDefinition;\n\n /**\n * Optional routing key for dead-lettered messages.\n */\n deadLetterRoutingKey?: string;\n\n /**\n * Maximum number of delivery attempts before dead-lettering.\n * @minimum 1\n */\n deliveryLimit: number;\n\n /**\n * If true, the queue is deleted when the last consumer unsubscribes.\n * @default false\n */\n autoDelete?: boolean;\n\n /**\n * Additional AMQP arguments for advanced configuration.\n */\n arguments?: Record<string, unknown>;\n};\n\n/**\n * Create a quorum queue with quorum-native retry.\n *\n * This is a simplified helper that enforces best practices:\n * - Uses quorum queues (recommended for most use cases)\n * - Requires dead letter exchange for failed message handling\n * - Uses quorum-native retry mode (simpler than TTL-backoff)\n *\n * **When to use:**\n * - You want simple, immediate retries without exponential backoff\n * - You don't need configurable delays between retries\n * - You want the simplest retry configuration\n *\n * @param name - The queue name\n * @param options - Configuration options\n * @returns A quorum queue definition with quorum-native retry\n *\n * @example\n * ```typescript\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n *\n * const orderQueue = defineQuorumQueue('order-processing', {\n * deadLetterExchange: dlx,\n * deliveryLimit: 3, // Retry up to 3 times\n * });\n *\n * const contract = defineContract({\n * exchanges: { dlx },\n * queues: { orderProcessing: orderQueue },\n * // ...\n * });\n * ```\n *\n * @see defineQueue - For full queue configuration options\n * @see defineTtlBackoffQueue - For queues with exponential backoff retry\n */\nexport function defineQuorumQueue(\n name: string,\n options: DefineQuorumQueueOptions,\n): QuorumQueueDefinition {\n const {\n deadLetterExchange,\n deadLetterRoutingKey,\n deliveryLimit,\n autoDelete,\n arguments: args,\n } = options;\n\n const deadLetter: DeadLetterConfig = deadLetterRoutingKey\n ? { exchange: deadLetterExchange, routingKey: deadLetterRoutingKey }\n : { exchange: deadLetterExchange };\n\n const queueOptions: QuorumQueueOptions = {\n type: \"quorum\",\n deadLetter,\n deliveryLimit,\n retry: { mode: \"quorum-native\" },\n };\n\n if (autoDelete !== undefined) queueOptions.autoDelete = autoDelete;\n if (args !== undefined) queueOptions.arguments = args;\n\n return defineQueue(name, queueOptions) as QuorumQueueDefinition;\n}\n\n/**\n * Options for creating a queue with TTL-backoff retry.\n *\n * This simplified helper enforces the required configuration for TTL-backoff retry:\n * - Dead letter exchange is required (used for retry routing)\n * - Returns infrastructure that includes wait queue and bindings\n */\nexport type DefineTtlBackoffQueueOptions = {\n /**\n * Dead letter exchange - required for TTL-backoff retry.\n * Used for routing messages to the wait queue and back.\n */\n deadLetterExchange: ExchangeDefinition;\n\n /**\n * Optional routing key for dead-lettered messages.\n */\n deadLetterRoutingKey?: string;\n\n /**\n * Maximum retry attempts before sending to DLQ.\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * Initial delay in ms before first retry.\n * @default 1000\n */\n initialDelayMs?: number;\n\n /**\n * Maximum delay in ms between retries.\n * @default 30000\n */\n maxDelayMs?: number;\n\n /**\n * Exponential backoff multiplier.\n * @default 2\n */\n backoffMultiplier?: number;\n\n /**\n * Add jitter to prevent thundering herd.\n * @default true\n */\n jitter?: boolean;\n\n /**\n * If true, the queue is deleted when the last consumer unsubscribes.\n * @default false\n */\n autoDelete?: boolean;\n\n /**\n * Additional AMQP arguments for advanced configuration.\n */\n arguments?: Record<string, unknown>;\n};\n\n/**\n * Create a queue with TTL-backoff retry (exponential backoff).\n *\n * This is a simplified helper that enforces best practices:\n * - Uses quorum queues (recommended for most use cases)\n * - Requires dead letter exchange for retry routing\n * - Uses TTL-backoff retry mode with configurable delays\n * - Automatically generates wait queue and bindings\n *\n * **When to use:**\n * - You need exponential backoff between retries\n * - You want configurable delays (initial delay, max delay, jitter)\n * - You're processing messages that may need time before retry\n *\n * **Returns:** A `QueueWithTtlBackoffInfrastructure` object that includes the\n * main queue, wait queue, and bindings. Pass this directly to `defineContract`\n * and it will be expanded automatically.\n *\n * @param name - The queue name\n * @param options - Configuration options\n * @returns A queue with TTL-backoff infrastructure\n *\n * @example\n * ```typescript\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n *\n * const orderQueue = defineTtlBackoffQueue('order-processing', {\n * deadLetterExchange: dlx,\n * maxRetries: 5,\n * initialDelayMs: 1000, // Start with 1s delay\n * maxDelayMs: 30000, // Cap at 30s\n * });\n *\n * const contract = defineContract({\n * exchanges: { dlx },\n * queues: { orderProcessing: orderQueue }, // Wait queue auto-added\n * // ... bindings auto-generated\n * });\n *\n * // To access the underlying queue definition (e.g., for the queue name):\n * import { extractQueue } from '@amqp-contract/contract';\n * const queueName = extractQueue(orderQueue).name;\n * ```\n *\n * @see defineQueue - For full queue configuration options\n * @see defineQuorumQueue - For queues with quorum-native retry (simpler, immediate retries)\n * @see extractQueue - To access the underlying queue definition\n */\nexport function defineTtlBackoffQueue(\n name: string,\n options: DefineTtlBackoffQueueOptions,\n): QueueWithTtlBackoffInfrastructure {\n const {\n deadLetterExchange,\n deadLetterRoutingKey,\n maxRetries,\n initialDelayMs,\n maxDelayMs,\n backoffMultiplier,\n jitter,\n autoDelete,\n arguments: args,\n } = options;\n\n const deadLetter: DeadLetterConfig = deadLetterRoutingKey\n ? { exchange: deadLetterExchange, routingKey: deadLetterRoutingKey }\n : { exchange: deadLetterExchange };\n\n // Build retry options, only including defined values\n const retryOptions: TtlBackoffRetryOptions = { mode: \"ttl-backoff\" };\n if (maxRetries !== undefined) retryOptions.maxRetries = maxRetries;\n if (initialDelayMs !== undefined) retryOptions.initialDelayMs = initialDelayMs;\n if (maxDelayMs !== undefined) retryOptions.maxDelayMs = maxDelayMs;\n if (backoffMultiplier !== undefined) retryOptions.backoffMultiplier = backoffMultiplier;\n if (jitter !== undefined) retryOptions.jitter = jitter;\n\n const queueOptions: QuorumQueueOptions = {\n type: \"quorum\",\n deadLetter,\n retry: retryOptions,\n };\n\n if (autoDelete !== undefined) queueOptions.autoDelete = autoDelete;\n if (args !== undefined) queueOptions.arguments = args;\n\n const result = defineQueue(name, queueOptions);\n\n // Since we configured TTL-backoff with a dead letter exchange, the result will\n // always be QueueWithTtlBackoffInfrastructure\n return result as QueueWithTtlBackoffInfrastructure;\n}\n","import type {\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n MessageDefinition,\n PublisherDefinition,\n TopicExchangeDefinition,\n} from \"../types.js\";\n\n/**\n * Define a message publisher for a fanout exchange.\n *\n * A publisher sends messages to an exchange. For fanout exchanges, messages are broadcast\n * to all bound queues regardless of routing key, so no routing key is required.\n *\n * The message schema is validated when publishing to ensure type safety.\n *\n * **Which pattern to use:**\n *\n * | Pattern | Best for | Description |\n * |---------|----------|-------------|\n * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |\n * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |\n * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |\n *\n * Use `defineEventPublisher` when:\n * - One publisher feeds multiple consumers\n * - You want automatic schema consistency between publisher and consumers\n * - You're building event-driven architectures\n *\n * @param exchange - The fanout exchange definition to publish to\n * @param message - The message definition with payload schema\n * @param options - Optional publisher configuration\n * @returns A publisher definition with inferred message types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const logsExchange = defineExchange('logs', 'fanout', { durable: true });\n * const logMessage = defineMessage(\n * z.object({\n * level: z.enum(['info', 'warn', 'error']),\n * message: z.string(),\n * timestamp: z.string().datetime(),\n * })\n * );\n *\n * const logPublisher = definePublisher(logsExchange, logMessage);\n * ```\n *\n * @see defineEventPublisher - For event-driven patterns with automatic schema consistency\n * @see defineCommandConsumer - For task queue patterns with automatic schema consistency\n */\nexport function definePublisher<TMessage extends MessageDefinition>(\n exchange: FanoutExchangeDefinition,\n message: TMessage,\n options?: Omit<\n Extract<PublisherDefinition<TMessage>, { exchange: FanoutExchangeDefinition }>,\n \"exchange\" | \"message\" | \"routingKey\"\n >,\n): Extract<PublisherDefinition<TMessage>, { exchange: FanoutExchangeDefinition }>;\n\n/**\n * Define a message publisher for a direct or topic exchange.\n *\n * A publisher sends messages to an exchange with a specific routing key.\n * The routing key determines which queues receive the message.\n *\n * The message schema is validated when publishing to ensure type safety.\n *\n * **Which pattern to use:**\n *\n * | Pattern | Best for | Description |\n * |---------|----------|-------------|\n * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |\n * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |\n * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |\n *\n * Use `defineEventPublisher` when:\n * - One publisher feeds multiple consumers\n * - You want automatic schema consistency between publisher and consumers\n * - You're building event-driven architectures\n *\n * @param exchange - The direct or topic exchange definition to publish to\n * @param message - The message definition with payload schema\n * @param options - Publisher configuration (routingKey is required)\n * @param options.routingKey - The routing key for message routing\n * @returns A publisher definition with inferred message types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string().uuid(),\n * amount: z.number().positive(),\n * }),\n * {\n * summary: 'Order created event',\n * description: 'Emitted when a new order is created'\n * }\n * );\n *\n * const orderCreatedPublisher = definePublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created'\n * });\n * ```\n *\n * @see defineEventPublisher - For event-driven patterns with automatic schema consistency\n * @see defineCommandConsumer - For task queue patterns with automatic schema consistency\n */\nexport function definePublisher<TMessage extends MessageDefinition>(\n exchange: DirectExchangeDefinition | TopicExchangeDefinition,\n message: TMessage,\n options: Omit<\n Extract<\n PublisherDefinition<TMessage>,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n >,\n \"exchange\" | \"message\"\n >,\n): Extract<\n PublisherDefinition<TMessage>,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n>;\n\n/**\n * Define a message publisher.\n *\n * This is the implementation function - use the type-specific overloads for better type safety.\n *\n * @param exchange - The exchange definition\n * @param message - The message definition\n * @param options - Optional publisher configuration\n * @returns A publisher definition\n * @internal\n */\nexport function definePublisher<TMessage extends MessageDefinition>(\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: { routingKey?: string },\n): PublisherDefinition<TMessage> {\n if (exchange.type === \"fanout\") {\n return {\n exchange,\n message,\n } as PublisherDefinition<TMessage>;\n }\n\n return {\n exchange,\n message,\n routingKey: options?.routingKey ?? \"\",\n } as PublisherDefinition<TMessage>;\n}\n\n/**\n * Helper to call definePublisher with proper type handling.\n * Type safety is enforced by overloaded public function signatures.\n * @internal\n */\nexport function definePublisherInternal<TMessage extends MessageDefinition>(\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): PublisherDefinition<TMessage> {\n // Type assertion is safe because overloaded signatures enforce routingKey requirement\n if (exchange.type === \"fanout\") {\n return definePublisher(exchange, message, options);\n }\n return definePublisher(exchange, message, options as { routingKey: string });\n}\n","import type { ConsumerDefinition, MessageDefinition, QueueEntry } from \"../types.js\";\nimport { extractQueue } from \"./queue.js\";\n\n/**\n * Define a message consumer.\n *\n * A consumer receives and processes messages from a queue. The message schema is validated\n * automatically when messages are consumed, ensuring type safety for your handlers.\n *\n * Consumers are associated with a specific queue and message type. When you create a worker\n * with this consumer, it will process messages from the queue according to the schema.\n *\n * **Which pattern to use:**\n *\n * | Pattern | Best for | Description |\n * |---------|----------|-------------|\n * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |\n * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |\n * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |\n *\n * Use `defineCommandConsumer` when:\n * - One consumer receives from multiple publishers\n * - You want automatic schema consistency between consumer and publishers\n * - You're building task queue or command patterns\n *\n * @param queue - The queue definition to consume from\n * @param message - The message definition with payload schema\n * @param options - Optional consumer configuration\n * @returns A consumer definition with inferred message types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string().uuid(),\n * customerId: z.string().uuid(),\n * amount: z.number().positive(),\n * })\n * );\n *\n * const processOrderConsumer = defineConsumer(orderQueue, orderMessage);\n *\n * // Later, when creating a worker, you'll provide a handler for this consumer:\n * // const worker = await TypedAmqpWorker.create({\n * // contract,\n * // handlers: {\n * // processOrder: async (message) => {\n * // // message is automatically typed based on the schema\n * // console.log(message.orderId); // string\n * // }\n * // },\n * // connection\n * // });\n * ```\n *\n * @see defineCommandConsumer - For task queue patterns with automatic schema consistency\n * @see defineEventPublisher - For event-driven patterns with automatic schema consistency\n */\nexport function defineConsumer<TMessage extends MessageDefinition>(\n queue: QueueEntry,\n message: TMessage,\n options?: Omit<ConsumerDefinition<TMessage>, \"queue\" | \"message\">,\n): ConsumerDefinition<TMessage> {\n return {\n queue: extractQueue(queue),\n message,\n ...options,\n };\n}\n","import type { BindingPattern, RoutingKey } from \"./routing-types.js\";\nimport type {\n ConsumerDefinition,\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n MessageDefinition,\n QueueBindingDefinition,\n QueueEntry,\n TopicExchangeDefinition,\n} from \"../types.js\";\nimport { defineConsumer } from \"./consumer.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Configuration for an event publisher.\n *\n * Events are published without knowing who consumes them. Multiple consumers\n * can subscribe to the same event. This follows the pub/sub pattern where\n * publishers broadcast events and consumers subscribe to receive them.\n *\n * @template TMessage - The message definition\n * @template TExchange - The exchange definition\n * @template TRoutingKey - The routing key type (undefined for fanout)\n */\nexport type EventPublisherConfig<\n TMessage extends MessageDefinition,\n TExchange extends ExchangeDefinition,\n TRoutingKey extends string | undefined = undefined,\n> = {\n /** Discriminator to identify this as an event publisher config */\n __brand: \"EventPublisherConfig\";\n /** The exchange to publish to */\n exchange: TExchange;\n /** The message definition */\n message: TMessage;\n /** The routing key for direct/topic exchanges */\n routingKey: TRoutingKey;\n /** Additional AMQP arguments */\n arguments?: Record<string, unknown>;\n};\n\n/**\n * Result from defineEventConsumer.\n *\n * Contains the consumer definition and binding needed to subscribe to an event.\n * Can be used directly in defineContract's consumers section - the binding\n * will be automatically extracted.\n *\n * @template TMessage - The message definition\n */\nexport type EventConsumerResult<TMessage extends MessageDefinition> = {\n /** Discriminator to identify this as an event consumer result */\n __brand: \"EventConsumerResult\";\n /** The consumer definition for processing messages */\n consumer: ConsumerDefinition<TMessage>;\n /** The binding connecting the queue to the exchange */\n binding: QueueBindingDefinition;\n};\n\n/**\n * Define an event publisher for broadcasting messages via fanout exchange.\n *\n * Events are published without knowing who consumes them. Multiple consumers\n * can subscribe to the same event using `defineEventConsumer`.\n *\n * @param exchange - The fanout exchange to publish to\n * @param message - The message definition (schema and metadata)\n * @returns An event publisher configuration\n *\n * @example\n * ```typescript\n * const logsExchange = defineExchange('logs', 'fanout', { durable: true });\n * const logMessage = defineMessage(z.object({\n * level: z.enum(['info', 'warn', 'error']),\n * message: z.string(),\n * }));\n *\n * // Create event publisher\n * const logEvent = defineEventPublisher(logsExchange, logMessage);\n *\n * // Multiple consumers can subscribe\n * const { consumer: fileConsumer, binding: fileBinding } =\n * defineEventConsumer(logEvent, fileLogsQueue);\n * const { consumer: alertConsumer, binding: alertBinding } =\n * defineEventConsumer(logEvent, alertsQueue);\n * ```\n */\nexport function defineEventPublisher<TMessage extends MessageDefinition>(\n exchange: FanoutExchangeDefinition,\n message: TMessage,\n): EventPublisherConfig<TMessage, FanoutExchangeDefinition, undefined>;\n\n/**\n * Define an event publisher for broadcasting messages via direct exchange.\n *\n * Events are published with a specific routing key. Consumers will receive\n * messages that match the routing key exactly.\n *\n * @param exchange - The direct exchange to publish to\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key\n * @param options.routingKey - The routing key for message routing\n * @param options.arguments - Additional AMQP arguments\n * @returns An event publisher configuration\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'direct', { durable: true });\n * const taskMessage = defineMessage(z.object({ taskId: z.string() }));\n *\n * const taskEvent = defineEventPublisher(tasksExchange, taskMessage, {\n * routingKey: 'task.execute',\n * });\n * ```\n */\nexport function defineEventPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n exchange: DirectExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: RoutingKey<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): EventPublisherConfig<TMessage, DirectExchangeDefinition, TRoutingKey>;\n\n/**\n * Define an event publisher for broadcasting messages via topic exchange.\n *\n * Events are published with a concrete routing key. Consumers can subscribe\n * using patterns (with * and # wildcards) to receive matching messages.\n *\n * @param exchange - The topic exchange to publish to\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key\n * @param options.routingKey - The concrete routing key (no wildcards)\n * @param options.arguments - Additional AMQP arguments\n * @returns An event publisher configuration\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderMessage = defineMessage(z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * }));\n *\n * // Publisher uses concrete routing key\n * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created',\n * });\n *\n * // Consumer can use pattern\n * const { consumer, binding } = defineEventConsumer(\n * orderCreatedEvent,\n * allOrdersQueue,\n * { routingKey: 'order.*' },\n * );\n * ```\n */\nexport function defineEventPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n exchange: TopicExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: RoutingKey<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): EventPublisherConfig<TMessage, TopicExchangeDefinition, TRoutingKey>;\n\n/**\n * Implementation of defineEventPublisher.\n * @internal\n */\nexport function defineEventPublisher<TMessage extends MessageDefinition>(\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): EventPublisherConfig<TMessage, ExchangeDefinition, string | undefined> {\n const config: EventPublisherConfig<TMessage, ExchangeDefinition, string | undefined> = {\n __brand: \"EventPublisherConfig\",\n exchange,\n message,\n routingKey: options?.routingKey,\n };\n\n if (options?.arguments !== undefined) {\n config.arguments = options.arguments;\n }\n\n return config;\n}\n\n/**\n * Create a consumer that subscribes to an event from a fanout exchange.\n *\n * @param eventPublisher - The event publisher configuration\n * @param queue - The queue that will receive messages\n * @param options - Optional binding configuration\n * @returns An object with the consumer definition and binding\n *\n * @example\n * ```typescript\n * const logEvent = defineEventPublisher(logsExchange, logMessage);\n * const { consumer, binding } = defineEventConsumer(logEvent, logsQueue);\n * ```\n */\nexport function defineEventConsumer<TMessage extends MessageDefinition>(\n eventPublisher: EventPublisherConfig<TMessage, FanoutExchangeDefinition, undefined>,\n queue: QueueEntry,\n options?: {\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage>;\n\n/**\n * Create a consumer that subscribes to an event from a direct exchange.\n *\n * @param eventPublisher - The event publisher configuration\n * @param queue - The queue that will receive messages\n * @param options - Optional binding configuration\n * @returns An object with the consumer definition and binding\n */\nexport function defineEventConsumer<TMessage extends MessageDefinition, TRoutingKey extends string>(\n eventPublisher: EventPublisherConfig<TMessage, DirectExchangeDefinition, TRoutingKey>,\n queue: QueueEntry,\n options?: {\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage>;\n\n/**\n * Create a consumer that subscribes to an event from a topic exchange.\n *\n * For topic exchanges, the consumer can optionally override the routing key\n * with a pattern to subscribe to multiple events.\n *\n * @param eventPublisher - The event publisher configuration\n * @param queue - The queue that will receive messages\n * @param options - Optional binding configuration\n * @param options.routingKey - Override routing key with pattern (defaults to publisher's key)\n * @returns An object with the consumer definition and binding\n *\n * @example\n * ```typescript\n * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created',\n * });\n *\n * // Use exact routing key from publisher\n * const { consumer: exactConsumer } = defineEventConsumer(orderCreatedEvent, exactQueue);\n *\n * // Override with pattern to receive all order events\n * const { consumer: allConsumer } = defineEventConsumer(orderCreatedEvent, allQueue, {\n * routingKey: 'order.*',\n * });\n * ```\n */\nexport function defineEventConsumer<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n TConsumerRoutingKey extends string = TRoutingKey,\n>(\n eventPublisher: EventPublisherConfig<TMessage, TopicExchangeDefinition, TRoutingKey>,\n queue: QueueEntry,\n options?: {\n routingKey?: BindingPattern<TConsumerRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage>;\n\n/**\n * Implementation of defineEventConsumer.\n * @internal\n */\nexport function defineEventConsumer<TMessage extends MessageDefinition>(\n eventPublisher: EventPublisherConfig<TMessage, ExchangeDefinition, string | undefined>,\n queue: QueueEntry,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage> {\n const { exchange, message, routingKey: publisherRoutingKey } = eventPublisher;\n\n // For topic exchanges, consumer can override the routing key\n const bindingRoutingKey = options?.routingKey ?? publisherRoutingKey;\n\n const bindingOptions: { routingKey?: string; arguments?: Record<string, unknown> } = {};\n if (bindingRoutingKey !== undefined) {\n bindingOptions.routingKey = bindingRoutingKey;\n }\n const bindingArguments = options?.arguments ?? eventPublisher.arguments;\n if (bindingArguments !== undefined) {\n bindingOptions.arguments = bindingArguments;\n }\n\n const binding = defineQueueBindingInternal(queue, exchange, bindingOptions);\n const consumer = defineConsumer(queue, message);\n\n return {\n __brand: \"EventConsumerResult\",\n consumer,\n binding,\n };\n}\n\n/**\n * Type guard to check if a value is an EventPublisherConfig.\n *\n * @param value - The value to check\n * @returns True if the value is an EventPublisherConfig\n */\nexport function isEventPublisherConfig(\n value: unknown,\n): value is EventPublisherConfig<MessageDefinition, ExchangeDefinition, string | undefined> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__brand\" in value &&\n value.__brand === \"EventPublisherConfig\"\n );\n}\n\n/**\n * Type guard to check if a value is an EventConsumerResult.\n *\n * @param value - The value to check\n * @returns True if the value is an EventConsumerResult\n */\nexport function isEventConsumerResult(\n value: unknown,\n): value is EventConsumerResult<MessageDefinition> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__brand\" in value &&\n value.__brand === \"EventConsumerResult\"\n );\n}\n","import type { BindingPattern, RoutingKey } from \"./routing-types.js\";\nimport type {\n ConsumerDefinition,\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n MessageDefinition,\n PublisherDefinition,\n QueueBindingDefinition,\n QueueEntry,\n TopicExchangeDefinition,\n} from \"../types.js\";\nimport { defineConsumer } from \"./consumer.js\";\nimport { definePublisherInternal } from \"./publisher.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Configuration for a command consumer.\n *\n * Commands are sent by one or more publishers to a single consumer (task queue pattern).\n * The consumer \"owns\" the queue, and publishers send commands to it.\n *\n * @template TMessage - The message definition\n * @template TExchange - The exchange definition\n * @template TRoutingKey - The routing key type (undefined for fanout)\n */\nexport type CommandConsumerConfig<\n TMessage extends MessageDefinition,\n TExchange extends ExchangeDefinition,\n TRoutingKey extends string | undefined = undefined,\n> = {\n /** Discriminator to identify this as a command consumer config */\n __brand: \"CommandConsumerConfig\";\n /** The consumer definition for processing commands */\n consumer: ConsumerDefinition<TMessage>;\n /** The binding connecting the queue to the exchange */\n binding: QueueBindingDefinition;\n /** The exchange that receives commands */\n exchange: TExchange;\n /** The message definition */\n message: TMessage;\n /** The routing key pattern for the binding */\n routingKey: TRoutingKey;\n};\n\n/**\n * Define a command consumer for receiving commands via fanout exchange.\n *\n * Commands are sent by publishers to a specific queue. The consumer \"owns\" the\n * queue and defines what commands it accepts.\n *\n * @param queue - The queue that will receive commands\n * @param exchange - The fanout exchange that routes commands\n * @param message - The message definition (schema and metadata)\n * @returns A command consumer configuration\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'fanout', { durable: true });\n * const taskMessage = defineMessage(z.object({ taskId: z.string() }));\n *\n * // Consumer owns the queue\n * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage);\n *\n * // Publishers send commands to it\n * const sendTask = defineCommandPublisher(executeTask);\n * ```\n */\nexport function defineCommandConsumer<TMessage extends MessageDefinition>(\n queue: QueueEntry,\n exchange: FanoutExchangeDefinition,\n message: TMessage,\n): CommandConsumerConfig<TMessage, FanoutExchangeDefinition, undefined>;\n\n/**\n * Define a command consumer for receiving commands via direct exchange.\n *\n * Commands are sent by publishers with a specific routing key that matches\n * the binding pattern.\n *\n * @param queue - The queue that will receive commands\n * @param exchange - The direct exchange that routes commands\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key\n * @param options.routingKey - The routing key for the binding\n * @param options.arguments - Additional AMQP arguments\n * @returns A command consumer configuration\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'direct', { durable: true });\n * const taskMessage = defineMessage(z.object({ taskId: z.string() }));\n *\n * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage, {\n * routingKey: 'task.execute',\n * });\n *\n * const sendTask = defineCommandPublisher(executeTask);\n * ```\n */\nexport function defineCommandConsumer<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n queue: QueueEntry,\n exchange: DirectExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: RoutingKey<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): CommandConsumerConfig<TMessage, DirectExchangeDefinition, TRoutingKey>;\n\n/**\n * Define a command consumer for receiving commands via topic exchange.\n *\n * The consumer binds with a routing key pattern (can use * and # wildcards).\n * Publishers then send commands with concrete routing keys that match the pattern.\n *\n * @param queue - The queue that will receive commands\n * @param exchange - The topic exchange that routes commands\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key pattern\n * @param options.routingKey - The routing key pattern for the binding\n * @param options.arguments - Additional AMQP arguments\n * @returns A command consumer configuration\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderMessage = defineMessage(z.object({ orderId: z.string() }));\n *\n * // Consumer uses pattern to receive multiple command types\n * const processOrder = defineCommandConsumer(orderQueue, ordersExchange, orderMessage, {\n * routingKey: 'order.*',\n * });\n *\n * // Publishers send with concrete keys\n * const createOrder = defineCommandPublisher(processOrder, {\n * routingKey: 'order.create',\n * });\n * const updateOrder = defineCommandPublisher(processOrder, {\n * routingKey: 'order.update',\n * });\n * ```\n */\nexport function defineCommandConsumer<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n queue: QueueEntry,\n exchange: TopicExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: BindingPattern<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): CommandConsumerConfig<TMessage, TopicExchangeDefinition, TRoutingKey>;\n\n/**\n * Implementation of defineCommandConsumer.\n * @internal\n */\nexport function defineCommandConsumer<TMessage extends MessageDefinition>(\n queue: QueueEntry,\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): CommandConsumerConfig<TMessage, ExchangeDefinition, string | undefined> {\n const consumer = defineConsumer(queue, message);\n const binding = defineQueueBindingInternal(queue, exchange, options);\n\n return {\n __brand: \"CommandConsumerConfig\",\n consumer,\n binding,\n exchange,\n message,\n routingKey: options?.routingKey,\n };\n}\n\n/**\n * Create a publisher that sends commands to a fanout exchange consumer.\n *\n * @param commandConsumer - The command consumer configuration\n * @returns A publisher definition\n *\n * @example\n * ```typescript\n * const executeTask = defineCommandConsumer(taskQueue, fanoutExchange, taskMessage);\n * const sendTask = defineCommandPublisher(executeTask);\n * ```\n */\nexport function defineCommandPublisher<TMessage extends MessageDefinition>(\n commandConsumer: CommandConsumerConfig<TMessage, FanoutExchangeDefinition, undefined>,\n): { message: TMessage; exchange: FanoutExchangeDefinition };\n\n/**\n * Create a publisher that sends commands to a direct exchange consumer.\n *\n * @param commandConsumer - The command consumer configuration\n * @returns A publisher definition\n */\nexport function defineCommandPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n commandConsumer: CommandConsumerConfig<TMessage, DirectExchangeDefinition, TRoutingKey>,\n): { message: TMessage; exchange: DirectExchangeDefinition; routingKey: string };\n\n/**\n * Create a publisher that sends commands to a topic exchange consumer.\n *\n * For topic exchanges where the consumer uses a pattern, the publisher can\n * optionally specify a concrete routing key that matches the pattern.\n *\n * @param commandConsumer - The command consumer configuration\n * @param options - Optional publisher configuration\n * @param options.routingKey - Override routing key (must match consumer's pattern)\n * @returns A publisher definition\n *\n * @example\n * ```typescript\n * // Consumer binds with pattern\n * const processOrder = defineCommandConsumer(orderQueue, topicExchange, orderMessage, {\n * routingKey: 'order.*',\n * });\n *\n * // Publisher uses concrete key matching the pattern\n * const createOrder = defineCommandPublisher(processOrder, {\n * routingKey: 'order.create',\n * });\n * ```\n */\nexport function defineCommandPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n TPublisherRoutingKey extends string = TRoutingKey,\n>(\n commandConsumer: CommandConsumerConfig<TMessage, TopicExchangeDefinition, TRoutingKey>,\n options?: {\n routingKey?: RoutingKey<TPublisherRoutingKey>;\n },\n): { message: TMessage; exchange: TopicExchangeDefinition; routingKey: string };\n\n/**\n * Implementation of defineCommandPublisher.\n * @internal\n */\nexport function defineCommandPublisher<TMessage extends MessageDefinition>(\n commandConsumer: CommandConsumerConfig<TMessage, ExchangeDefinition, string | undefined>,\n options?: {\n routingKey?: string;\n },\n): PublisherDefinition<TMessage> {\n const { exchange, message, routingKey: consumerRoutingKey } = commandConsumer;\n\n // For topic exchanges, publisher can override the routing key\n const publisherRoutingKey = options?.routingKey ?? consumerRoutingKey;\n\n const publisherOptions: { routingKey?: string } = {};\n if (publisherRoutingKey !== undefined) {\n publisherOptions.routingKey = publisherRoutingKey;\n }\n\n return definePublisherInternal(exchange, message, publisherOptions);\n}\n\n/**\n * Type guard to check if a value is a CommandConsumerConfig.\n *\n * @param value - The value to check\n * @returns True if the value is a CommandConsumerConfig\n */\nexport function isCommandConsumerConfig(\n value: unknown,\n): value is CommandConsumerConfig<MessageDefinition, ExchangeDefinition, string | undefined> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__brand\" in value &&\n value.__brand === \"CommandConsumerConfig\"\n );\n}\n","import type {\n BindingDefinition,\n ConsumerDefinition,\n ContractDefinition,\n ContractDefinitionInput,\n PublisherDefinition,\n QueueDefinition,\n} from \"../types.js\";\nimport { isEventConsumerResult, isEventPublisherConfig } from \"./event.js\";\nimport { definePublisherInternal } from \"./publisher.js\";\nimport { isCommandConsumerConfig } from \"./command.js\";\nimport { isQueueWithTtlBackoffInfrastructure } from \"./queue.js\";\n\n/**\n * Type utility to produce the output contract type from the input.\n * The output has the same structure but with all entries normalized to\n * their base definition types (PublisherDefinition, ConsumerDefinition).\n */\ntype ContractOutput<TContract extends ContractDefinitionInput> = TContract;\n\n/**\n * Define an AMQP contract.\n *\n * A contract is the central definition of your AMQP messaging topology. It brings together\n * all exchanges, queues, bindings, publishers, and consumers in a single, type-safe definition.\n *\n * The contract is used by both clients (for publishing) and workers (for consuming) to ensure\n * type safety throughout your messaging infrastructure. TypeScript will infer all message types\n * and publisher/consumer names from the contract.\n *\n * @param definition - The contract definition containing all AMQP resources\n * @param definition.exchanges - Named exchange definitions\n * @param definition.queues - Named queue definitions\n * @param definition.bindings - Named binding definitions (queue-to-exchange or exchange-to-exchange)\n * @param definition.publishers - Named publisher definitions for sending messages\n * @param definition.consumers - Named consumer definitions for receiving messages\n * @returns The same contract definition with full type inference\n *\n * @example\n * ```typescript\n * import {\n * defineContract,\n * defineExchange,\n * defineQueue,\n * defineQueueBinding,\n * definePublisher,\n * defineConsumer,\n * defineMessage,\n * } from '@amqp-contract/contract';\n * import { z } from 'zod';\n *\n * // Define resources\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * })\n * );\n *\n * // Compose contract\n * export const contract = defineContract({\n * exchanges: {\n * orders: ordersExchange,\n * },\n * queues: {\n * orderProcessing: orderQueue,\n * },\n * bindings: {\n * orderBinding: defineQueueBinding(orderQueue, ordersExchange, {\n * routingKey: 'order.created',\n * }),\n * },\n * publishers: {\n * orderCreated: definePublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created',\n * }),\n * },\n * consumers: {\n * processOrder: defineConsumer(orderQueue, orderMessage),\n * },\n * });\n *\n * // TypeScript now knows:\n * // - client.publish('orderCreated', { orderId: string, amount: number })\n * // - handler: async (message: { orderId: string, amount: number }) => void\n * ```\n */\nexport function defineContract<TContract extends ContractDefinitionInput>(\n definition: TContract,\n): ContractOutput<TContract> {\n // Exclude publishers and consumers from spread since they may contain config entries\n const { publishers: inputPublishers, consumers: inputConsumers, ...rest } = definition;\n const result: ContractDefinition = rest as ContractDefinition;\n\n // Process queues to extract TTL-backoff infrastructure\n if (definition.queues && Object.keys(definition.queues).length > 0) {\n const expandedQueues: Record<string, QueueDefinition> = {};\n const queueBindings: Record<string, BindingDefinition> = {};\n\n for (const [name, entry] of Object.entries(definition.queues)) {\n if (isQueueWithTtlBackoffInfrastructure(entry)) {\n expandedQueues[name] = entry.queue;\n expandedQueues[`${name}Wait`] = entry.waitQueue;\n queueBindings[`${name}WaitBinding`] = entry.waitQueueBinding;\n queueBindings[`${name}RetryBinding`] = entry.mainQueueRetryBinding;\n } else {\n expandedQueues[name] = entry as QueueDefinition;\n }\n }\n\n result.queues = expandedQueues;\n\n if (Object.keys(queueBindings).length > 0) {\n result.bindings = { ...result.bindings, ...queueBindings };\n }\n }\n\n // Process publishers section - extract EventPublisherConfig entries\n if (inputPublishers && Object.keys(inputPublishers).length > 0) {\n const processedPublishers: Record<string, PublisherDefinition> = {};\n\n for (const [name, entry] of Object.entries(inputPublishers)) {\n if (isEventPublisherConfig(entry)) {\n // EventPublisherConfig: extract to publisher definition\n const publisherOptions: { routingKey?: string } = {};\n if (entry.routingKey !== undefined) {\n publisherOptions.routingKey = entry.routingKey;\n }\n processedPublishers[name] = definePublisherInternal(\n entry.exchange,\n entry.message,\n publisherOptions,\n );\n } else {\n // Plain PublisherDefinition\n processedPublishers[name] = entry as PublisherDefinition;\n }\n }\n\n result.publishers = processedPublishers;\n }\n\n // Process consumers section - extract EventConsumerResult and CommandConsumerConfig entries\n if (inputConsumers && Object.keys(inputConsumers).length > 0) {\n const processedConsumers: Record<string, ConsumerDefinition> = {};\n const consumerBindings: Record<string, BindingDefinition> = {};\n\n for (const [name, entry] of Object.entries(inputConsumers)) {\n if (isEventConsumerResult(entry)) {\n // EventConsumerResult: extract consumer and binding\n processedConsumers[name] = entry.consumer;\n consumerBindings[`${name}Binding`] = entry.binding;\n } else if (isCommandConsumerConfig(entry)) {\n // CommandConsumerConfig: extract consumer and binding\n processedConsumers[name] = entry.consumer;\n consumerBindings[`${name}Binding`] = entry.binding;\n } else {\n // Plain ConsumerDefinition\n processedConsumers[name] = entry as ConsumerDefinition;\n }\n }\n\n result.consumers = processedConsumers;\n\n if (Object.keys(consumerBindings).length > 0) {\n result.bindings = { ...result.bindings, ...consumerBindings };\n }\n }\n\n return result as ContractOutput<TContract>;\n}\n","import type { QueueBindingDefinition, QueueDefinition, QueueEntry } from \"../types.js\";\nimport { defineQueue, extractQueue } from \"./queue.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Result type for TTL-backoff retry infrastructure builder.\n *\n * Contains the wait queue and bindings needed for TTL-backoff retry.\n */\nexport type TtlBackoffRetryInfrastructure = {\n /**\n * The wait queue for holding messages during backoff delay.\n * This is a classic queue with a dead letter exchange pointing back to the main queue.\n */\n waitQueue: QueueDefinition;\n /**\n * Binding that routes failed messages to the wait queue.\n */\n waitQueueBinding: QueueBindingDefinition;\n /**\n * Binding that routes retried messages back to the main queue.\n */\n mainQueueRetryBinding: QueueBindingDefinition;\n};\n\n/**\n * Create TTL-backoff retry infrastructure for a queue.\n *\n * This builder helper generates the wait queue and bindings needed for TTL-backoff retry.\n * The generated infrastructure can be spread into a contract definition.\n *\n * TTL-backoff retry works by:\n * 1. Failed messages are sent to the DLX with routing key `{queueName}-wait`\n * 2. The wait queue receives these messages and holds them for a TTL period\n * 3. After TTL expires, messages are dead-lettered back to the DLX with routing key `{queueName}`\n * 4. The main queue receives the retried message via its binding to the DLX\n *\n * @param queue - The main queue definition (must have deadLetter configured)\n * @param options - Optional configuration for the wait queue\n * @param options.waitQueueDurable - Whether the wait queue should be durable (default: same as main queue)\n * @returns TTL-backoff retry infrastructure containing wait queue and bindings\n * @throws {Error} If the queue does not have a dead letter exchange configured\n *\n * @example\n * ```typescript\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n * const orderQueue = defineQueue('order-processing', {\n * type: 'quorum',\n * deadLetter: { exchange: dlx },\n * retry: {\n * mode: 'ttl-backoff',\n * maxRetries: 5,\n * initialDelayMs: 1000,\n * },\n * });\n *\n * // Generate TTL-backoff infrastructure\n * const retryInfra = defineTtlBackoffRetryInfrastructure(orderQueue);\n *\n * // Spread into contract\n * const contract = defineContract({\n * exchanges: { dlx },\n * queues: {\n * orderProcessing: orderQueue,\n * orderProcessingWait: retryInfra.waitQueue,\n * },\n * bindings: {\n * ...// your other bindings\n * orderWaitBinding: retryInfra.waitQueueBinding,\n * orderRetryBinding: retryInfra.mainQueueRetryBinding,\n * },\n * // ... publishers and consumers\n * });\n * ```\n */\nexport function defineTtlBackoffRetryInfrastructure(\n queueEntry: QueueEntry,\n options?: {\n waitQueueDurable?: boolean;\n },\n): TtlBackoffRetryInfrastructure {\n const queue = extractQueue(queueEntry);\n if (!queue.deadLetter) {\n throw new Error(\n `Queue \"${queue.name}\" does not have a dead letter exchange configured. ` +\n `TTL-backoff retry requires deadLetter to be set on the queue.`,\n );\n }\n\n const dlx = queue.deadLetter.exchange;\n const waitQueueName = `${queue.name}-wait`;\n\n // Create the wait queue - quorum for better durability\n // Wait queue uses default TTL-backoff retry (infrastructure queue, not directly consumed)\n const waitQueue = defineQueue(waitQueueName, {\n type: \"quorum\",\n durable: options?.waitQueueDurable ?? queue.durable ?? true,\n deadLetter: {\n exchange: dlx,\n routingKey: queue.name, // Routes back to main queue after TTL\n },\n }) as QueueDefinition;\n\n // Create binding for wait queue to receive failed messages\n const waitQueueBinding = defineQueueBindingInternal(waitQueue, dlx, {\n routingKey: waitQueueName,\n });\n\n // Create binding for main queue to receive retried messages\n const mainQueueRetryBinding = defineQueueBindingInternal(queue, dlx, {\n routingKey: queue.name,\n });\n\n return {\n waitQueue,\n waitQueueBinding,\n mainQueueRetryBinding,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["isQueueWithTtlBackoffInfrastructure","retry","queueDefinition"],"sources":["../src/builder/exchange.ts","../src/builder/message.ts","../src/builder/binding.ts","../src/builder/queue.ts","../src/builder/publisher.ts","../src/builder/consumer.ts","../src/builder/event.ts","../src/builder/command.ts","../src/builder/contract.ts","../src/builder/ttl-backoff.ts"],"sourcesContent":["import type {\n BaseExchangeDefinition,\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n TopicExchangeDefinition,\n} from \"../types.js\";\n\n/**\n * Define a fanout exchange.\n *\n * A fanout exchange routes messages to all bound queues without considering routing keys.\n * This exchange type is ideal for broadcasting messages to multiple consumers.\n *\n * @param name - The name of the exchange\n * @param type - Must be \"fanout\"\n * @param options - Optional exchange configuration\n * @param options.durable - If true, the exchange survives broker restarts (default: false)\n * @param options.autoDelete - If true, the exchange is deleted when no queues are bound (default: false)\n * @param options.internal - If true, the exchange cannot be directly published to (default: false)\n * @param options.arguments - Additional AMQP arguments for the exchange\n * @returns A fanout exchange definition\n *\n * @example\n * ```typescript\n * const logsExchange = defineExchange('logs', 'fanout', {\n * durable: true\n * });\n * ```\n */\nexport function defineExchange(\n name: string,\n type: \"fanout\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): FanoutExchangeDefinition;\n\n/**\n * Define a direct exchange.\n *\n * A direct exchange routes messages to queues based on exact routing key matches.\n * This exchange type is ideal for point-to-point messaging.\n *\n * @param name - The name of the exchange\n * @param type - Must be \"direct\"\n * @param options - Optional exchange configuration\n * @param options.durable - If true, the exchange survives broker restarts (default: false)\n * @param options.autoDelete - If true, the exchange is deleted when no queues are bound (default: false)\n * @param options.internal - If true, the exchange cannot be directly published to (default: false)\n * @param options.arguments - Additional AMQP arguments for the exchange\n * @returns A direct exchange definition\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'direct', {\n * durable: true\n * });\n * ```\n */\nexport function defineExchange(\n name: string,\n type: \"direct\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): DirectExchangeDefinition;\n\n/**\n * Define a topic exchange.\n *\n * A topic exchange routes messages to queues based on routing key patterns.\n * Routing keys can use wildcards: `*` matches one word, `#` matches zero or more words.\n * This exchange type is ideal for flexible message routing based on hierarchical topics.\n *\n * @param name - The name of the exchange\n * @param type - Must be \"topic\"\n * @param options - Optional exchange configuration\n * @param options.durable - If true, the exchange survives broker restarts (default: false)\n * @param options.autoDelete - If true, the exchange is deleted when no queues are bound (default: false)\n * @param options.internal - If true, the exchange cannot be directly published to (default: false)\n * @param options.arguments - Additional AMQP arguments for the exchange\n * @returns A topic exchange definition\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', {\n * durable: true\n * });\n * ```\n */\nexport function defineExchange(\n name: string,\n type: \"topic\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): TopicExchangeDefinition;\n\n/**\n * Define an AMQP exchange.\n *\n * An exchange receives messages from publishers and routes them to queues based on the exchange type\n * and routing rules. This is the implementation function - use the type-specific overloads for better\n * type safety.\n *\n * @param name - The name of the exchange\n * @param type - The type of exchange: \"fanout\", \"direct\", or \"topic\"\n * @param options - Optional exchange configuration\n * @returns An exchange definition\n * @internal\n */\nexport function defineExchange(\n name: string,\n type: \"fanout\" | \"direct\" | \"topic\",\n options?: Omit<BaseExchangeDefinition, \"name\" | \"type\">,\n): ExchangeDefinition {\n return {\n name,\n type,\n ...options,\n };\n}\n","import type { MessageDefinition } from \"../types.js\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Define a message definition with payload and optional headers/metadata.\n *\n * A message definition specifies the schema for message payloads and headers using\n * Standard Schema v1 compatible libraries (Zod, Valibot, ArkType, etc.).\n * The schemas are used for automatic validation when publishing or consuming messages.\n *\n * @param payload - The payload schema (must be Standard Schema v1 compatible)\n * @param options - Optional message metadata\n * @param options.headers - Optional header schema for message headers\n * @param options.summary - Brief description for documentation (used in AsyncAPI generation)\n * @param options.description - Detailed description for documentation (used in AsyncAPI generation)\n * @returns A message definition with inferred types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string().uuid(),\n * customerId: z.string().uuid(),\n * amount: z.number().positive(),\n * items: z.array(z.object({\n * productId: z.string(),\n * quantity: z.number().int().positive(),\n * })),\n * }),\n * {\n * summary: 'Order created event',\n * description: 'Emitted when a new order is created in the system'\n * }\n * );\n * ```\n */\nexport function defineMessage<\n TPayload extends MessageDefinition[\"payload\"],\n THeaders extends StandardSchemaV1<Record<string, unknown>> | undefined = undefined,\n>(\n payload: TPayload,\n options?: {\n headers?: THeaders;\n summary?: string;\n description?: string;\n },\n): MessageDefinition<TPayload, THeaders> {\n return {\n payload,\n ...options,\n };\n}\n","import type {\n DirectExchangeDefinition,\n ExchangeBindingDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n QueueBindingDefinition,\n QueueDefinition,\n QueueEntry,\n QueueWithTtlBackoffInfrastructure,\n TopicExchangeDefinition,\n} from \"../types.js\";\n\n/**\n * Type guard to check if a queue entry is a QueueWithTtlBackoffInfrastructure.\n * Duplicated here to avoid circular dependency with queue.ts.\n * @internal\n */\nfunction isQueueWithTtlBackoffInfrastructure(\n entry: QueueEntry,\n): entry is QueueWithTtlBackoffInfrastructure {\n return (\n typeof entry === \"object\" &&\n entry !== null &&\n \"__brand\" in entry &&\n entry.__brand === \"QueueWithTtlBackoffInfrastructure\"\n );\n}\n\n/**\n * Extract the plain QueueDefinition from a QueueEntry.\n * Duplicated here to avoid circular dependency with queue.ts.\n * @internal\n */\nfunction extractQueueInternal(entry: QueueEntry): QueueDefinition {\n if (isQueueWithTtlBackoffInfrastructure(entry)) {\n return entry.queue;\n }\n return entry;\n}\n\n/**\n * Define a binding between a queue and a fanout exchange.\n *\n * Binds a queue to a fanout exchange to receive all messages published to the exchange.\n * Fanout exchanges ignore routing keys, so this overload doesn't require one.\n *\n * @param queue - The queue definition or queue with infrastructure to bind\n * @param exchange - The fanout exchange definition\n * @param options - Optional binding configuration\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns A queue binding definition\n *\n * @example\n * ```typescript\n * const logsQueue = defineQueue('logs-queue', { durable: true });\n * const logsExchange = defineExchange('logs', 'fanout', { durable: true });\n *\n * const binding = defineQueueBinding(logsQueue, logsExchange);\n * ```\n */\nexport function defineQueueBinding(\n queue: QueueEntry,\n exchange: FanoutExchangeDefinition,\n options?: Omit<\n Extract<QueueBindingDefinition, { exchange: FanoutExchangeDefinition }>,\n \"type\" | \"queue\" | \"exchange\" | \"routingKey\"\n >,\n): Extract<QueueBindingDefinition, { exchange: FanoutExchangeDefinition }>;\n\n/**\n * Define a binding between a queue and a direct or topic exchange.\n *\n * Binds a queue to an exchange with a specific routing key pattern.\n * Messages are only routed to the queue if the routing key matches the pattern.\n *\n * For direct exchanges: The routing key must match exactly.\n * For topic exchanges: The routing key can include wildcards:\n * - `*` matches exactly one word\n * - `#` matches zero or more words\n *\n * @param queue - The queue definition or queue with infrastructure to bind\n * @param exchange - The direct or topic exchange definition\n * @param options - Binding configuration (routingKey is required)\n * @param options.routingKey - The routing key pattern for message routing\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns A queue binding definition\n *\n * @example\n * ```typescript\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n *\n * // Bind with exact routing key\n * const binding = defineQueueBinding(orderQueue, ordersExchange, {\n * routingKey: 'order.created'\n * });\n *\n * // Bind with wildcard pattern\n * const allOrdersBinding = defineQueueBinding(orderQueue, ordersExchange, {\n * routingKey: 'order.*' // Matches order.created, order.updated, etc.\n * });\n * ```\n */\nexport function defineQueueBinding(\n queue: QueueEntry,\n exchange: DirectExchangeDefinition | TopicExchangeDefinition,\n options: Omit<\n Extract<\n QueueBindingDefinition,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n >,\n \"type\" | \"queue\" | \"exchange\"\n >,\n): Extract<\n QueueBindingDefinition,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n>;\n\n/**\n * Define a binding between a queue and an exchange.\n *\n * This is the implementation function - use the type-specific overloads for better type safety.\n *\n * @param queue - The queue definition or queue with infrastructure to bind\n * @param exchange - The exchange definition\n * @param options - Optional binding configuration\n * @returns A queue binding definition\n * @internal\n */\nexport function defineQueueBinding(\n queue: QueueEntry,\n exchange: ExchangeDefinition,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): QueueBindingDefinition {\n // Extract the plain queue definition from QueueEntry\n const queueDef = extractQueueInternal(queue);\n\n if (exchange.type === \"fanout\") {\n return {\n type: \"queue\",\n queue: queueDef,\n exchange,\n ...(options?.arguments && { arguments: options.arguments }),\n } as QueueBindingDefinition;\n }\n\n return {\n type: \"queue\",\n queue: queueDef,\n exchange,\n routingKey: options?.routingKey,\n ...(options?.arguments && { arguments: options.arguments }),\n } as QueueBindingDefinition;\n}\n\n/**\n * Internal helper to call defineQueueBinding with proper type handling.\n * Used by queue.ts to avoid circular dependency.\n * @internal\n */\nexport function defineQueueBindingInternal(\n queue: QueueEntry,\n exchange: ExchangeDefinition,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): QueueBindingDefinition {\n if (exchange.type === \"fanout\") {\n return defineQueueBinding(queue, exchange, options);\n }\n return defineQueueBinding(queue, exchange, options as { routingKey: string });\n}\n\n/**\n * Define a binding between two exchanges (exchange-to-exchange routing).\n *\n * Binds a destination exchange to a fanout source exchange.\n * Messages published to the source exchange will be forwarded to the destination exchange.\n * Fanout exchanges ignore routing keys, so this overload doesn't require one.\n *\n * @param destination - The destination exchange definition\n * @param source - The fanout source exchange definition\n * @param options - Optional binding configuration\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns An exchange binding definition\n *\n * @example\n * ```typescript\n * const sourceExchange = defineExchange('logs', 'fanout', { durable: true });\n * const destExchange = defineExchange('all-logs', 'fanout', { durable: true });\n *\n * const binding = defineExchangeBinding(destExchange, sourceExchange);\n * ```\n */\nexport function defineExchangeBinding(\n destination: ExchangeDefinition,\n source: FanoutExchangeDefinition,\n options?: Omit<\n Extract<ExchangeBindingDefinition, { source: FanoutExchangeDefinition }>,\n \"type\" | \"source\" | \"destination\" | \"routingKey\"\n >,\n): Extract<ExchangeBindingDefinition, { source: FanoutExchangeDefinition }>;\n\n/**\n * Define a binding between two exchanges (exchange-to-exchange routing).\n *\n * Binds a destination exchange to a direct or topic source exchange with a routing key pattern.\n * Messages are forwarded from source to destination only if the routing key matches the pattern.\n *\n * @param destination - The destination exchange definition\n * @param source - The direct or topic source exchange definition\n * @param options - Binding configuration (routingKey is required)\n * @param options.routingKey - The routing key pattern for message routing\n * @param options.arguments - Additional AMQP arguments for the binding\n * @returns An exchange binding definition\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const importantExchange = defineExchange('important-orders', 'topic', { durable: true });\n *\n * // Forward only high-value orders\n * const binding = defineExchangeBinding(importantExchange, ordersExchange, {\n * routingKey: 'order.high-value.*'\n * });\n * ```\n */\nexport function defineExchangeBinding(\n destination: ExchangeDefinition,\n source: DirectExchangeDefinition | TopicExchangeDefinition,\n options: Omit<\n Extract<\n ExchangeBindingDefinition,\n { source: DirectExchangeDefinition | TopicExchangeDefinition }\n >,\n \"type\" | \"source\" | \"destination\"\n >,\n): Extract<\n ExchangeBindingDefinition,\n { source: DirectExchangeDefinition | TopicExchangeDefinition }\n>;\n\n/**\n * Define a binding between two exchanges (exchange-to-exchange routing).\n *\n * This is the implementation function - use the type-specific overloads for better type safety.\n *\n * @param destination - The destination exchange definition\n * @param source - The source exchange definition\n * @param options - Optional binding configuration\n * @returns An exchange binding definition\n * @internal\n */\nexport function defineExchangeBinding(\n destination: ExchangeDefinition,\n source: ExchangeDefinition,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): ExchangeBindingDefinition {\n if (source.type === \"fanout\") {\n return {\n type: \"exchange\",\n source,\n destination,\n ...(options?.arguments && { arguments: options.arguments }),\n } as ExchangeBindingDefinition;\n }\n\n return {\n type: \"exchange\",\n source,\n destination,\n routingKey: options?.routingKey ?? \"\",\n ...(options?.arguments && { arguments: options.arguments }),\n } as ExchangeBindingDefinition;\n}\n","import type {\n ClassicQueueDefinition,\n ClassicQueueOptions,\n DeadLetterConfig,\n DefineQueueOptions,\n ExchangeDefinition,\n QueueDefinition,\n QueueEntry,\n QueueWithTtlBackoffInfrastructure,\n QuorumQueueDefinition,\n QuorumQueueOptions,\n ResolvedTtlBackoffRetryOptions,\n TtlBackoffRetryOptions,\n} from \"../types.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Resolve TTL-backoff retry options with defaults applied.\n * @internal\n */\nexport function resolveTtlBackoffOptions(\n options: TtlBackoffRetryOptions | undefined,\n): ResolvedTtlBackoffRetryOptions {\n return {\n mode: \"ttl-backoff\",\n maxRetries: options?.maxRetries ?? 3,\n initialDelayMs: options?.initialDelayMs ?? 1000,\n maxDelayMs: options?.maxDelayMs ?? 30000,\n backoffMultiplier: options?.backoffMultiplier ?? 2,\n jitter: options?.jitter ?? true,\n };\n}\n\n/**\n * Type guard to check if a queue entry is a QueueWithTtlBackoffInfrastructure.\n *\n * When you configure a queue with TTL-backoff retry and a dead letter exchange,\n * `defineQueue` returns a `QueueWithTtlBackoffInfrastructure` instead of a plain\n * `QueueDefinition`. This type guard helps you distinguish between the two.\n *\n * **When to use:**\n * - When you need to check the type of a queue entry at runtime\n * - When writing generic code that handles both plain queues and infrastructure wrappers\n *\n * **Related functions:**\n * - `extractQueue()` - Use this to get the underlying queue definition from either type\n *\n * @param entry - The queue entry to check\n * @returns True if the entry is a QueueWithTtlBackoffInfrastructure, false otherwise\n *\n * @example\n * ```typescript\n * const queue = defineQueue('orders', {\n * deadLetter: { exchange: dlx },\n * retry: { mode: 'ttl-backoff' },\n * });\n *\n * if (isQueueWithTtlBackoffInfrastructure(queue)) {\n * // queue has .queue, .waitQueue, .waitQueueBinding, .mainQueueRetryBinding\n * console.log('Wait queue:', queue.waitQueue.name);\n * } else {\n * // queue is a plain QueueDefinition\n * console.log('Queue:', queue.name);\n * }\n * ```\n */\nexport function isQueueWithTtlBackoffInfrastructure(\n entry: QueueEntry,\n): entry is QueueWithTtlBackoffInfrastructure {\n return (\n typeof entry === \"object\" &&\n entry !== null &&\n \"__brand\" in entry &&\n entry.__brand === \"QueueWithTtlBackoffInfrastructure\"\n );\n}\n\n/**\n * Extract the plain QueueDefinition from a QueueEntry.\n *\n * **Why this function exists:**\n * When you configure a queue with TTL-backoff retry and a dead letter exchange,\n * `defineQueue` (or `defineTtlBackoffQueue`) returns a wrapper object that includes\n * the main queue, wait queue, and bindings. This function extracts the underlying\n * queue definition so you can access properties like `name`, `type`, etc.\n *\n * **When to use:**\n * - When you need to access queue properties (name, type, deadLetter, etc.)\n * - When passing a queue to functions that expect a plain QueueDefinition\n * - Works safely on both plain queues and infrastructure wrappers\n *\n * **How it works:**\n * - If the entry is a `QueueWithTtlBackoffInfrastructure`, returns `entry.queue`\n * - Otherwise, returns the entry as-is (it's already a plain QueueDefinition)\n *\n * @param entry - The queue entry (either plain QueueDefinition or QueueWithTtlBackoffInfrastructure)\n * @returns The plain QueueDefinition\n *\n * @example\n * ```typescript\n * import { defineQueue, defineTtlBackoffQueue, extractQueue } from '@amqp-contract/contract';\n *\n * // TTL-backoff queue returns a wrapper\n * const orderQueue = defineTtlBackoffQueue('orders', {\n * deadLetterExchange: dlx,\n * maxRetries: 3,\n * });\n *\n * // Use extractQueue to access the queue name\n * const queueName = extractQueue(orderQueue).name; // 'orders'\n *\n * // Also works safely on plain queues\n * const plainQueue = defineQueue('simple', { type: 'quorum', retry: { mode: 'quorum-native' } });\n * const plainName = extractQueue(plainQueue).name; // 'simple'\n *\n * // Access other properties\n * const queueDef = extractQueue(orderQueue);\n * console.log(queueDef.name); // 'orders'\n * console.log(queueDef.type); // 'quorum'\n * console.log(queueDef.deadLetter); // { exchange: dlx, ... }\n * ```\n *\n * @see isQueueWithTtlBackoffInfrastructure - Type guard to check if extraction is needed\n * @see defineTtlBackoffQueue - Creates queues with TTL-backoff infrastructure\n */\nexport function extractQueue(entry: QueueEntry): QueueDefinition {\n if (isQueueWithTtlBackoffInfrastructure(entry)) {\n return entry.queue;\n }\n return entry;\n}\n\n/**\n * Wrap a queue definition with TTL-backoff retry infrastructure.\n * @internal\n */\nfunction wrapWithTtlBackoffInfrastructure(\n queue: QueueDefinition,\n): QueueWithTtlBackoffInfrastructure {\n if (!queue.deadLetter) {\n throw new Error(\n `Queue \"${queue.name}\" does not have a dead letter exchange configured. ` +\n `TTL-backoff retry requires deadLetter to be set on the queue.`,\n );\n }\n\n const dlx = queue.deadLetter.exchange;\n const waitQueueName = `${queue.name}-wait`;\n\n // Create the wait queue - quorum for better durability\n // Wait queue uses TTL-backoff mode (infrastructure queue, not directly consumed)\n const waitQueue: QuorumQueueDefinition = {\n name: waitQueueName,\n type: \"quorum\",\n durable: queue.durable ?? true,\n deadLetter: {\n exchange: dlx,\n routingKey: queue.name, // Routes back to main queue after TTL\n },\n retry: resolveTtlBackoffOptions(undefined),\n };\n\n // Create binding for wait queue to receive failed messages\n const waitQueueBinding = defineQueueBindingInternal(waitQueue, dlx, {\n routingKey: waitQueueName,\n });\n\n // Create binding for main queue to receive retried messages\n const mainQueueRetryBinding = defineQueueBindingInternal(queue, dlx, {\n routingKey: queue.name,\n });\n\n return {\n __brand: \"QueueWithTtlBackoffInfrastructure\",\n queue,\n waitQueue,\n waitQueueBinding,\n mainQueueRetryBinding,\n };\n}\n\n/**\n * Define an AMQP queue.\n *\n * A queue stores messages until they are consumed by workers. Queues can be bound to exchanges\n * to receive messages based on routing rules.\n *\n * By default, queues are created as quorum queues which provide better durability and\n * high-availability. Use `type: 'classic'` for special cases like non-durable queues\n * or priority queues.\n *\n * @param name - The name of the queue\n * @param options - Optional queue configuration\n * @param options.type - Queue type: 'quorum' (default, recommended) or 'classic'\n * @param options.durable - If true, the queue survives broker restarts. Quorum queues are always durable.\n * @param options.exclusive - If true, the queue can only be used by the declaring connection. Only supported with classic queues.\n * @param options.autoDelete - If true, the queue is deleted when the last consumer unsubscribes (default: false)\n * @param options.deadLetter - Dead letter configuration for handling failed messages\n * @param options.maxPriority - Maximum priority level for priority queue (1-255, recommended: 1-10). Only supported with classic queues.\n * @param options.arguments - Additional AMQP arguments (e.g., x-message-ttl)\n * @returns A queue definition\n *\n * @example\n * ```typescript\n * // Quorum queue (default, recommended for production)\n * const orderQueue = defineQueue('order-processing');\n *\n * // Explicit quorum queue with dead letter exchange\n * const dlx = defineExchange('orders-dlx', 'topic', { durable: true });\n * const orderQueueWithDLX = defineQueue('order-processing', {\n * type: 'quorum',\n * deadLetter: {\n * exchange: dlx,\n * routingKey: 'order.failed'\n * },\n * arguments: {\n * 'x-message-ttl': 86400000, // 24 hours\n * }\n * });\n *\n * // Classic queue (for special cases)\n * const tempQueue = defineQueue('temp-queue', {\n * type: 'classic',\n * durable: false,\n * autoDelete: true,\n * });\n *\n * // Priority queue (requires classic type)\n * const taskQueue = defineQueue('urgent-tasks', {\n * type: 'classic',\n * durable: true,\n * maxPriority: 10,\n * });\n *\n * // Queue with TTL-backoff retry (returns infrastructure automatically)\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n * const orderQueue = defineQueue('order-processing', {\n * deadLetter: { exchange: dlx },\n * retry: { mode: 'ttl-backoff', maxRetries: 5 },\n * });\n * // orderQueue is QueueWithTtlBackoffInfrastructure, pass directly to defineContract\n * ```\n */\nexport function defineQueue(\n name: string,\n options?: DefineQueueOptions,\n): QueueDefinition | QueueWithTtlBackoffInfrastructure {\n const opts = options ?? {};\n const type = opts.type ?? \"quorum\";\n\n // Build base properties shared by both queue types\n const baseProps: {\n name: string;\n durable?: boolean;\n autoDelete?: boolean;\n deadLetter?: DeadLetterConfig;\n arguments?: Record<string, unknown>;\n } = { name };\n\n if (opts.durable !== undefined) {\n baseProps.durable = opts.durable;\n }\n\n if (opts.autoDelete !== undefined) {\n baseProps.autoDelete = opts.autoDelete;\n }\n\n if (opts.deadLetter !== undefined) {\n baseProps.deadLetter = opts.deadLetter;\n }\n\n if (opts.arguments !== undefined) {\n baseProps.arguments = opts.arguments;\n }\n\n // Build quorum queue\n if (type === \"quorum\") {\n const quorumOpts = opts as QuorumQueueOptions;\n const inputRetry = quorumOpts.retry ?? { mode: \"ttl-backoff\" as const };\n\n // Validate quorum-native retry requirements\n if (inputRetry.mode === \"quorum-native\") {\n if (quorumOpts.deliveryLimit === undefined) {\n throw new Error(\n `Queue \"${name}\" uses quorum-native retry mode but deliveryLimit is not configured. ` +\n `Quorum-native retry requires deliveryLimit to be set.`,\n );\n }\n }\n\n // Resolve retry options: apply defaults for TTL-backoff, keep quorum-native as-is\n const retry =\n inputRetry.mode === \"quorum-native\" ? inputRetry : resolveTtlBackoffOptions(inputRetry);\n\n const queueDefinition: QuorumQueueDefinition = {\n ...baseProps,\n type: \"quorum\",\n retry,\n };\n\n // Validate and add deliveryLimit\n if (quorumOpts.deliveryLimit !== undefined) {\n if (quorumOpts.deliveryLimit < 1 || !Number.isInteger(quorumOpts.deliveryLimit)) {\n throw new Error(\n `Invalid deliveryLimit: ${quorumOpts.deliveryLimit}. Must be a positive integer.`,\n );\n }\n queueDefinition.deliveryLimit = quorumOpts.deliveryLimit;\n }\n\n // If TTL-backoff retry with dead letter exchange, wrap with infrastructure\n if (retry.mode === \"ttl-backoff\" && queueDefinition.deadLetter) {\n return wrapWithTtlBackoffInfrastructure(queueDefinition);\n }\n\n return queueDefinition;\n }\n\n // Build classic queue\n const classicOpts = opts as ClassicQueueOptions;\n\n // Classic queues cannot use quorum-native retry mode\n if ((classicOpts.retry as { mode?: string } | undefined)?.mode === \"quorum-native\") {\n throw new Error(\n `Queue \"${name}\" uses quorum-native retry mode but is a classic queue. ` +\n `Quorum-native retry requires quorum queues (type: \"quorum\").`,\n );\n }\n\n // Resolve TTL-backoff options with defaults\n const retry = resolveTtlBackoffOptions(classicOpts.retry);\n\n const queueDefinition: ClassicQueueDefinition = {\n ...baseProps,\n type: \"classic\",\n retry,\n };\n\n // Add exclusive\n if (classicOpts.exclusive !== undefined) {\n queueDefinition.exclusive = classicOpts.exclusive;\n }\n\n // Validate and add maxPriority argument\n if (classicOpts.maxPriority !== undefined) {\n if (classicOpts.maxPriority < 1 || classicOpts.maxPriority > 255) {\n throw new Error(\n `Invalid maxPriority: ${classicOpts.maxPriority}. Must be between 1 and 255. Recommended range: 1-10.`,\n );\n }\n queueDefinition.arguments = {\n ...queueDefinition.arguments,\n \"x-max-priority\": classicOpts.maxPriority,\n };\n }\n\n // If TTL-backoff retry with dead letter exchange, wrap with infrastructure\n if (retry.mode === \"ttl-backoff\" && queueDefinition.deadLetter) {\n return wrapWithTtlBackoffInfrastructure(queueDefinition);\n }\n\n return queueDefinition;\n}\n\n// =============================================================================\n// Simplified Queue Configuration Helpers\n// =============================================================================\n\n/**\n * Options for creating a quorum queue with quorum-native retry.\n *\n * This simplified helper enforces the required configuration for quorum-native retry:\n * - Dead letter exchange is required (for failed messages)\n * - Delivery limit is required (for retry count)\n */\nexport type DefineQuorumQueueOptions = {\n /**\n * Dead letter configuration - required for retry support.\n * Failed messages will be sent to this exchange.\n */\n deadLetterExchange: ExchangeDefinition;\n\n /**\n * Optional routing key for dead-lettered messages.\n */\n deadLetterRoutingKey?: string;\n\n /**\n * Maximum number of delivery attempts before dead-lettering.\n * @minimum 1\n */\n deliveryLimit: number;\n\n /**\n * If true, the queue is deleted when the last consumer unsubscribes.\n * @default false\n */\n autoDelete?: boolean;\n\n /**\n * Additional AMQP arguments for advanced configuration.\n */\n arguments?: Record<string, unknown>;\n};\n\n/**\n * Create a quorum queue with quorum-native retry.\n *\n * This is a simplified helper that enforces best practices:\n * - Uses quorum queues (recommended for most use cases)\n * - Requires dead letter exchange for failed message handling\n * - Uses quorum-native retry mode (simpler than TTL-backoff)\n *\n * **When to use:**\n * - You want simple, immediate retries without exponential backoff\n * - You don't need configurable delays between retries\n * - You want the simplest retry configuration\n *\n * @param name - The queue name\n * @param options - Configuration options\n * @returns A quorum queue definition with quorum-native retry\n *\n * @example\n * ```typescript\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n *\n * const orderQueue = defineQuorumQueue('order-processing', {\n * deadLetterExchange: dlx,\n * deliveryLimit: 3, // Retry up to 3 times\n * });\n *\n * const contract = defineContract({\n * exchanges: { dlx },\n * queues: { orderProcessing: orderQueue },\n * // ...\n * });\n * ```\n *\n * @see defineQueue - For full queue configuration options\n * @see defineTtlBackoffQueue - For queues with exponential backoff retry\n */\nexport function defineQuorumQueue(\n name: string,\n options: DefineQuorumQueueOptions,\n): QuorumQueueDefinition {\n const {\n deadLetterExchange,\n deadLetterRoutingKey,\n deliveryLimit,\n autoDelete,\n arguments: args,\n } = options;\n\n const deadLetter: DeadLetterConfig = deadLetterRoutingKey\n ? { exchange: deadLetterExchange, routingKey: deadLetterRoutingKey }\n : { exchange: deadLetterExchange };\n\n const queueOptions: QuorumQueueOptions = {\n type: \"quorum\",\n deadLetter,\n deliveryLimit,\n retry: { mode: \"quorum-native\" },\n };\n\n if (autoDelete !== undefined) queueOptions.autoDelete = autoDelete;\n if (args !== undefined) queueOptions.arguments = args;\n\n return defineQueue(name, queueOptions) as QuorumQueueDefinition;\n}\n\n/**\n * Options for creating a queue with TTL-backoff retry.\n *\n * This simplified helper enforces the required configuration for TTL-backoff retry:\n * - Dead letter exchange is required (used for retry routing)\n * - Returns infrastructure that includes wait queue and bindings\n */\nexport type DefineTtlBackoffQueueOptions = {\n /**\n * Dead letter exchange - required for TTL-backoff retry.\n * Used for routing messages to the wait queue and back.\n */\n deadLetterExchange: ExchangeDefinition;\n\n /**\n * Optional routing key for dead-lettered messages.\n */\n deadLetterRoutingKey?: string;\n\n /**\n * Maximum retry attempts before sending to DLQ.\n * @default 3\n */\n maxRetries?: number;\n\n /**\n * Initial delay in ms before first retry.\n * @default 1000\n */\n initialDelayMs?: number;\n\n /**\n * Maximum delay in ms between retries.\n * @default 30000\n */\n maxDelayMs?: number;\n\n /**\n * Exponential backoff multiplier.\n * @default 2\n */\n backoffMultiplier?: number;\n\n /**\n * Add jitter to prevent thundering herd.\n * @default true\n */\n jitter?: boolean;\n\n /**\n * If true, the queue is deleted when the last consumer unsubscribes.\n * @default false\n */\n autoDelete?: boolean;\n\n /**\n * Additional AMQP arguments for advanced configuration.\n */\n arguments?: Record<string, unknown>;\n};\n\n/**\n * Create a queue with TTL-backoff retry (exponential backoff).\n *\n * This is a simplified helper that enforces best practices:\n * - Uses quorum queues (recommended for most use cases)\n * - Requires dead letter exchange for retry routing\n * - Uses TTL-backoff retry mode with configurable delays\n * - Automatically generates wait queue and bindings\n *\n * **When to use:**\n * - You need exponential backoff between retries\n * - You want configurable delays (initial delay, max delay, jitter)\n * - You're processing messages that may need time before retry\n *\n * **Returns:** A `QueueWithTtlBackoffInfrastructure` object that includes the\n * main queue, wait queue, and bindings. Pass this directly to `defineContract`\n * and it will be expanded automatically.\n *\n * @param name - The queue name\n * @param options - Configuration options\n * @returns A queue with TTL-backoff infrastructure\n *\n * @example\n * ```typescript\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n *\n * const orderQueue = defineTtlBackoffQueue('order-processing', {\n * deadLetterExchange: dlx,\n * maxRetries: 5,\n * initialDelayMs: 1000, // Start with 1s delay\n * maxDelayMs: 30000, // Cap at 30s\n * });\n *\n * const contract = defineContract({\n * exchanges: { dlx },\n * queues: { orderProcessing: orderQueue }, // Wait queue auto-added\n * // ... bindings auto-generated\n * });\n *\n * // To access the underlying queue definition (e.g., for the queue name):\n * import { extractQueue } from '@amqp-contract/contract';\n * const queueName = extractQueue(orderQueue).name;\n * ```\n *\n * @see defineQueue - For full queue configuration options\n * @see defineQuorumQueue - For queues with quorum-native retry (simpler, immediate retries)\n * @see extractQueue - To access the underlying queue definition\n */\nexport function defineTtlBackoffQueue(\n name: string,\n options: DefineTtlBackoffQueueOptions,\n): QueueWithTtlBackoffInfrastructure {\n const {\n deadLetterExchange,\n deadLetterRoutingKey,\n maxRetries,\n initialDelayMs,\n maxDelayMs,\n backoffMultiplier,\n jitter,\n autoDelete,\n arguments: args,\n } = options;\n\n const deadLetter: DeadLetterConfig = deadLetterRoutingKey\n ? { exchange: deadLetterExchange, routingKey: deadLetterRoutingKey }\n : { exchange: deadLetterExchange };\n\n // Build retry options, only including defined values\n const retryOptions: TtlBackoffRetryOptions = { mode: \"ttl-backoff\" };\n if (maxRetries !== undefined) retryOptions.maxRetries = maxRetries;\n if (initialDelayMs !== undefined) retryOptions.initialDelayMs = initialDelayMs;\n if (maxDelayMs !== undefined) retryOptions.maxDelayMs = maxDelayMs;\n if (backoffMultiplier !== undefined) retryOptions.backoffMultiplier = backoffMultiplier;\n if (jitter !== undefined) retryOptions.jitter = jitter;\n\n const queueOptions: QuorumQueueOptions = {\n type: \"quorum\",\n deadLetter,\n retry: retryOptions,\n };\n\n if (autoDelete !== undefined) queueOptions.autoDelete = autoDelete;\n if (args !== undefined) queueOptions.arguments = args;\n\n const result = defineQueue(name, queueOptions);\n\n // Since we configured TTL-backoff with a dead letter exchange, the result will\n // always be QueueWithTtlBackoffInfrastructure\n return result as QueueWithTtlBackoffInfrastructure;\n}\n","import type {\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n MessageDefinition,\n PublisherDefinition,\n TopicExchangeDefinition,\n} from \"../types.js\";\n\n/**\n * Define a message publisher for a fanout exchange.\n *\n * A publisher sends messages to an exchange. For fanout exchanges, messages are broadcast\n * to all bound queues regardless of routing key, so no routing key is required.\n *\n * The message schema is validated when publishing to ensure type safety.\n *\n * **Which pattern to use:**\n *\n * | Pattern | Best for | Description |\n * |---------|----------|-------------|\n * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |\n * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |\n * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |\n *\n * Use `defineEventPublisher` when:\n * - One publisher feeds multiple consumers\n * - You want automatic schema consistency between publisher and consumers\n * - You're building event-driven architectures\n *\n * @param exchange - The fanout exchange definition to publish to\n * @param message - The message definition with payload schema\n * @param options - Optional publisher configuration\n * @returns A publisher definition with inferred message types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const logsExchange = defineExchange('logs', 'fanout', { durable: true });\n * const logMessage = defineMessage(\n * z.object({\n * level: z.enum(['info', 'warn', 'error']),\n * message: z.string(),\n * timestamp: z.string().datetime(),\n * })\n * );\n *\n * const logPublisher = definePublisher(logsExchange, logMessage);\n * ```\n *\n * @see defineEventPublisher - For event-driven patterns with automatic schema consistency\n * @see defineCommandConsumer - For task queue patterns with automatic schema consistency\n */\nexport function definePublisher<TMessage extends MessageDefinition>(\n exchange: FanoutExchangeDefinition,\n message: TMessage,\n options?: Omit<\n Extract<PublisherDefinition<TMessage>, { exchange: FanoutExchangeDefinition }>,\n \"exchange\" | \"message\" | \"routingKey\"\n >,\n): Extract<PublisherDefinition<TMessage>, { exchange: FanoutExchangeDefinition }>;\n\n/**\n * Define a message publisher for a direct or topic exchange.\n *\n * A publisher sends messages to an exchange with a specific routing key.\n * The routing key determines which queues receive the message.\n *\n * The message schema is validated when publishing to ensure type safety.\n *\n * **Which pattern to use:**\n *\n * | Pattern | Best for | Description |\n * |---------|----------|-------------|\n * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |\n * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |\n * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |\n *\n * Use `defineEventPublisher` when:\n * - One publisher feeds multiple consumers\n * - You want automatic schema consistency between publisher and consumers\n * - You're building event-driven architectures\n *\n * @param exchange - The direct or topic exchange definition to publish to\n * @param message - The message definition with payload schema\n * @param options - Publisher configuration (routingKey is required)\n * @param options.routingKey - The routing key for message routing\n * @returns A publisher definition with inferred message types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string().uuid(),\n * amount: z.number().positive(),\n * }),\n * {\n * summary: 'Order created event',\n * description: 'Emitted when a new order is created'\n * }\n * );\n *\n * const orderCreatedPublisher = definePublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created'\n * });\n * ```\n *\n * @see defineEventPublisher - For event-driven patterns with automatic schema consistency\n * @see defineCommandConsumer - For task queue patterns with automatic schema consistency\n */\nexport function definePublisher<TMessage extends MessageDefinition>(\n exchange: DirectExchangeDefinition | TopicExchangeDefinition,\n message: TMessage,\n options: Omit<\n Extract<\n PublisherDefinition<TMessage>,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n >,\n \"exchange\" | \"message\"\n >,\n): Extract<\n PublisherDefinition<TMessage>,\n { exchange: DirectExchangeDefinition | TopicExchangeDefinition }\n>;\n\n/**\n * Define a message publisher.\n *\n * This is the implementation function - use the type-specific overloads for better type safety.\n *\n * @param exchange - The exchange definition\n * @param message - The message definition\n * @param options - Optional publisher configuration\n * @returns A publisher definition\n * @internal\n */\nexport function definePublisher<TMessage extends MessageDefinition>(\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: { routingKey?: string },\n): PublisherDefinition<TMessage> {\n if (exchange.type === \"fanout\") {\n return {\n exchange,\n message,\n } as PublisherDefinition<TMessage>;\n }\n\n return {\n exchange,\n message,\n routingKey: options?.routingKey ?? \"\",\n } as PublisherDefinition<TMessage>;\n}\n\n/**\n * Helper to call definePublisher with proper type handling.\n * Type safety is enforced by overloaded public function signatures.\n * @internal\n */\nexport function definePublisherInternal<TMessage extends MessageDefinition>(\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): PublisherDefinition<TMessage> {\n // Type assertion is safe because overloaded signatures enforce routingKey requirement\n if (exchange.type === \"fanout\") {\n return definePublisher(exchange, message, options);\n }\n return definePublisher(exchange, message, options as { routingKey: string });\n}\n","import type {\n CommandConsumerConfigBase,\n ConsumerDefinition,\n ConsumerEntry,\n EventConsumerResultBase,\n MessageDefinition,\n QueueEntry,\n} from \"../types.js\";\nimport { extractQueue } from \"./queue.js\";\n\n/**\n * Type guard to check if an entry is an EventConsumerResult.\n */\nfunction isEventConsumerResultEntry(entry: ConsumerEntry): entry is EventConsumerResultBase {\n return \"__brand\" in entry && entry.__brand === \"EventConsumerResult\";\n}\n\n/**\n * Type guard to check if an entry is a CommandConsumerConfig.\n */\nfunction isCommandConsumerConfigEntry(entry: ConsumerEntry): entry is CommandConsumerConfigBase {\n return \"__brand\" in entry && entry.__brand === \"CommandConsumerConfig\";\n}\n\n/**\n * Extract the ConsumerDefinition from any ConsumerEntry type.\n *\n * Handles the following entry types:\n * - ConsumerDefinition: returned as-is\n * - EventConsumerResult: returns the nested `.consumer` property\n * - CommandConsumerConfig: returns the nested `.consumer` property\n *\n * Use this function when you need to access the underlying ConsumerDefinition\n * from a consumer entry that may have been created with defineEventConsumer\n * or defineCommandConsumer.\n *\n * @param entry - The consumer entry to extract from\n * @returns The underlying ConsumerDefinition\n *\n * @example\n * ```typescript\n * // Works with plain ConsumerDefinition\n * const consumer1 = defineConsumer(queue, message);\n * extractConsumer(consumer1).queue.name; // \"my-queue\"\n *\n * // Works with EventConsumerResult\n * const consumer2 = defineEventConsumer(eventPublisher, queue);\n * extractConsumer(consumer2).queue.name; // \"my-queue\"\n *\n * // Works with CommandConsumerConfig\n * const consumer3 = defineCommandConsumer(queue, exchange, message, { routingKey: \"cmd\" });\n * extractConsumer(consumer3).queue.name; // \"my-queue\"\n * ```\n */\nexport function extractConsumer(entry: ConsumerEntry): ConsumerDefinition {\n if (isEventConsumerResultEntry(entry) || isCommandConsumerConfigEntry(entry)) {\n return entry.consumer;\n }\n // Otherwise it's a plain ConsumerDefinition\n return entry;\n}\n\n/**\n * Define a message consumer.\n *\n * A consumer receives and processes messages from a queue. The message schema is validated\n * automatically when messages are consumed, ensuring type safety for your handlers.\n *\n * Consumers are associated with a specific queue and message type. When you create a worker\n * with this consumer, it will process messages from the queue according to the schema.\n *\n * **Which pattern to use:**\n *\n * | Pattern | Best for | Description |\n * |---------|----------|-------------|\n * | `definePublisher` + `defineConsumer` | Independent definition | Define publishers and consumers separately with manual schema consistency |\n * | `defineEventPublisher` + `defineEventConsumer` | Event broadcasting | Define event publisher first, create consumers that subscribe to it |\n * | `defineCommandConsumer` + `defineCommandPublisher` | Task queues | Define command consumer first, create publishers that send commands to it |\n *\n * Use `defineCommandConsumer` when:\n * - One consumer receives from multiple publishers\n * - You want automatic schema consistency between consumer and publishers\n * - You're building task queue or command patterns\n *\n * @param queue - The queue definition to consume from\n * @param message - The message definition with payload schema\n * @param options - Optional consumer configuration\n * @returns A consumer definition with inferred message types\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n *\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string().uuid(),\n * customerId: z.string().uuid(),\n * amount: z.number().positive(),\n * })\n * );\n *\n * const processOrderConsumer = defineConsumer(orderQueue, orderMessage);\n *\n * // Later, when creating a worker, you'll provide a handler for this consumer:\n * // const worker = await TypedAmqpWorker.create({\n * // contract,\n * // handlers: {\n * // processOrder: async (message) => {\n * // // message is automatically typed based on the schema\n * // console.log(message.orderId); // string\n * // }\n * // },\n * // connection\n * // });\n * ```\n *\n * @see defineCommandConsumer - For task queue patterns with automatic schema consistency\n * @see defineEventPublisher - For event-driven patterns with automatic schema consistency\n */\nexport function defineConsumer<TMessage extends MessageDefinition>(\n queue: QueueEntry,\n message: TMessage,\n options?: Omit<ConsumerDefinition<TMessage>, \"queue\" | \"message\">,\n): ConsumerDefinition<TMessage> {\n return {\n queue: extractQueue(queue),\n message,\n ...options,\n };\n}\n","import type { BindingPattern, RoutingKey } from \"./routing-types.js\";\nimport type {\n ConsumerDefinition,\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n MessageDefinition,\n QueueBindingDefinition,\n QueueEntry,\n TopicExchangeDefinition,\n} from \"../types.js\";\nimport { defineConsumer } from \"./consumer.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Configuration for an event publisher.\n *\n * Events are published without knowing who consumes them. Multiple consumers\n * can subscribe to the same event. This follows the pub/sub pattern where\n * publishers broadcast events and consumers subscribe to receive them.\n *\n * @template TMessage - The message definition\n * @template TExchange - The exchange definition\n * @template TRoutingKey - The routing key type (undefined for fanout)\n */\nexport type EventPublisherConfig<\n TMessage extends MessageDefinition,\n TExchange extends ExchangeDefinition,\n TRoutingKey extends string | undefined = undefined,\n> = {\n /** Discriminator to identify this as an event publisher config */\n __brand: \"EventPublisherConfig\";\n /** The exchange to publish to */\n exchange: TExchange;\n /** The message definition */\n message: TMessage;\n /** The routing key for direct/topic exchanges */\n routingKey: TRoutingKey;\n /** Additional AMQP arguments */\n arguments?: Record<string, unknown>;\n};\n\n/**\n * Result from defineEventConsumer.\n *\n * Contains the consumer definition and binding needed to subscribe to an event.\n * Can be used directly in defineContract's consumers section - the binding\n * will be automatically extracted.\n *\n * @template TMessage - The message definition\n */\nexport type EventConsumerResult<TMessage extends MessageDefinition> = {\n /** Discriminator to identify this as an event consumer result */\n __brand: \"EventConsumerResult\";\n /** The consumer definition for processing messages */\n consumer: ConsumerDefinition<TMessage>;\n /** The binding connecting the queue to the exchange */\n binding: QueueBindingDefinition;\n};\n\n/**\n * Define an event publisher for broadcasting messages via fanout exchange.\n *\n * Events are published without knowing who consumes them. Multiple consumers\n * can subscribe to the same event using `defineEventConsumer`.\n *\n * @param exchange - The fanout exchange to publish to\n * @param message - The message definition (schema and metadata)\n * @returns An event publisher configuration\n *\n * @example\n * ```typescript\n * const logsExchange = defineExchange('logs', 'fanout', { durable: true });\n * const logMessage = defineMessage(z.object({\n * level: z.enum(['info', 'warn', 'error']),\n * message: z.string(),\n * }));\n *\n * // Create event publisher\n * const logEvent = defineEventPublisher(logsExchange, logMessage);\n *\n * // Multiple consumers can subscribe\n * const { consumer: fileConsumer, binding: fileBinding } =\n * defineEventConsumer(logEvent, fileLogsQueue);\n * const { consumer: alertConsumer, binding: alertBinding } =\n * defineEventConsumer(logEvent, alertsQueue);\n * ```\n */\nexport function defineEventPublisher<TMessage extends MessageDefinition>(\n exchange: FanoutExchangeDefinition,\n message: TMessage,\n): EventPublisherConfig<TMessage, FanoutExchangeDefinition, undefined>;\n\n/**\n * Define an event publisher for broadcasting messages via direct exchange.\n *\n * Events are published with a specific routing key. Consumers will receive\n * messages that match the routing key exactly.\n *\n * @param exchange - The direct exchange to publish to\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key\n * @param options.routingKey - The routing key for message routing\n * @param options.arguments - Additional AMQP arguments\n * @returns An event publisher configuration\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'direct', { durable: true });\n * const taskMessage = defineMessage(z.object({ taskId: z.string() }));\n *\n * const taskEvent = defineEventPublisher(tasksExchange, taskMessage, {\n * routingKey: 'task.execute',\n * });\n * ```\n */\nexport function defineEventPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n exchange: DirectExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: RoutingKey<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): EventPublisherConfig<TMessage, DirectExchangeDefinition, TRoutingKey>;\n\n/**\n * Define an event publisher for broadcasting messages via topic exchange.\n *\n * Events are published with a concrete routing key. Consumers can subscribe\n * using patterns (with * and # wildcards) to receive matching messages.\n *\n * @param exchange - The topic exchange to publish to\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key\n * @param options.routingKey - The concrete routing key (no wildcards)\n * @param options.arguments - Additional AMQP arguments\n * @returns An event publisher configuration\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderMessage = defineMessage(z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * }));\n *\n * // Publisher uses concrete routing key\n * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created',\n * });\n *\n * // Consumer can use pattern\n * const { consumer, binding } = defineEventConsumer(\n * orderCreatedEvent,\n * allOrdersQueue,\n * { routingKey: 'order.*' },\n * );\n * ```\n */\nexport function defineEventPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n exchange: TopicExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: RoutingKey<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): EventPublisherConfig<TMessage, TopicExchangeDefinition, TRoutingKey>;\n\n/**\n * Implementation of defineEventPublisher.\n * @internal\n */\nexport function defineEventPublisher<TMessage extends MessageDefinition>(\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): EventPublisherConfig<TMessage, ExchangeDefinition, string | undefined> {\n const config: EventPublisherConfig<TMessage, ExchangeDefinition, string | undefined> = {\n __brand: \"EventPublisherConfig\",\n exchange,\n message,\n routingKey: options?.routingKey,\n };\n\n if (options?.arguments !== undefined) {\n config.arguments = options.arguments;\n }\n\n return config;\n}\n\n/**\n * Create a consumer that subscribes to an event from a fanout exchange.\n *\n * @param eventPublisher - The event publisher configuration\n * @param queue - The queue that will receive messages\n * @param options - Optional binding configuration\n * @returns An object with the consumer definition and binding\n *\n * @example\n * ```typescript\n * const logEvent = defineEventPublisher(logsExchange, logMessage);\n * const { consumer, binding } = defineEventConsumer(logEvent, logsQueue);\n * ```\n */\nexport function defineEventConsumer<TMessage extends MessageDefinition>(\n eventPublisher: EventPublisherConfig<TMessage, FanoutExchangeDefinition, undefined>,\n queue: QueueEntry,\n options?: {\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage>;\n\n/**\n * Create a consumer that subscribes to an event from a direct exchange.\n *\n * @param eventPublisher - The event publisher configuration\n * @param queue - The queue that will receive messages\n * @param options - Optional binding configuration\n * @returns An object with the consumer definition and binding\n */\nexport function defineEventConsumer<TMessage extends MessageDefinition, TRoutingKey extends string>(\n eventPublisher: EventPublisherConfig<TMessage, DirectExchangeDefinition, TRoutingKey>,\n queue: QueueEntry,\n options?: {\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage>;\n\n/**\n * Create a consumer that subscribes to an event from a topic exchange.\n *\n * For topic exchanges, the consumer can optionally override the routing key\n * with a pattern to subscribe to multiple events.\n *\n * @param eventPublisher - The event publisher configuration\n * @param queue - The queue that will receive messages\n * @param options - Optional binding configuration\n * @param options.routingKey - Override routing key with pattern (defaults to publisher's key)\n * @returns An object with the consumer definition and binding\n *\n * @example\n * ```typescript\n * const orderCreatedEvent = defineEventPublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created',\n * });\n *\n * // Use exact routing key from publisher\n * const { consumer: exactConsumer } = defineEventConsumer(orderCreatedEvent, exactQueue);\n *\n * // Override with pattern to receive all order events\n * const { consumer: allConsumer } = defineEventConsumer(orderCreatedEvent, allQueue, {\n * routingKey: 'order.*',\n * });\n * ```\n */\nexport function defineEventConsumer<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n TConsumerRoutingKey extends string = TRoutingKey,\n>(\n eventPublisher: EventPublisherConfig<TMessage, TopicExchangeDefinition, TRoutingKey>,\n queue: QueueEntry,\n options?: {\n routingKey?: BindingPattern<TConsumerRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage>;\n\n/**\n * Implementation of defineEventConsumer.\n * @internal\n */\nexport function defineEventConsumer<TMessage extends MessageDefinition>(\n eventPublisher: EventPublisherConfig<TMessage, ExchangeDefinition, string | undefined>,\n queue: QueueEntry,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): EventConsumerResult<TMessage> {\n const { exchange, message, routingKey: publisherRoutingKey } = eventPublisher;\n\n // For topic exchanges, consumer can override the routing key\n const bindingRoutingKey = options?.routingKey ?? publisherRoutingKey;\n\n const bindingOptions: { routingKey?: string; arguments?: Record<string, unknown> } = {};\n if (bindingRoutingKey !== undefined) {\n bindingOptions.routingKey = bindingRoutingKey;\n }\n const bindingArguments = options?.arguments ?? eventPublisher.arguments;\n if (bindingArguments !== undefined) {\n bindingOptions.arguments = bindingArguments;\n }\n\n const binding = defineQueueBindingInternal(queue, exchange, bindingOptions);\n const consumer = defineConsumer(queue, message);\n\n return {\n __brand: \"EventConsumerResult\",\n consumer,\n binding,\n };\n}\n\n/**\n * Type guard to check if a value is an EventPublisherConfig.\n *\n * @param value - The value to check\n * @returns True if the value is an EventPublisherConfig\n */\nexport function isEventPublisherConfig(\n value: unknown,\n): value is EventPublisherConfig<MessageDefinition, ExchangeDefinition, string | undefined> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__brand\" in value &&\n value.__brand === \"EventPublisherConfig\"\n );\n}\n\n/**\n * Type guard to check if a value is an EventConsumerResult.\n *\n * @param value - The value to check\n * @returns True if the value is an EventConsumerResult\n */\nexport function isEventConsumerResult(\n value: unknown,\n): value is EventConsumerResult<MessageDefinition> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__brand\" in value &&\n value.__brand === \"EventConsumerResult\"\n );\n}\n","import type { BindingPattern, RoutingKey } from \"./routing-types.js\";\nimport type {\n ConsumerDefinition,\n DirectExchangeDefinition,\n ExchangeDefinition,\n FanoutExchangeDefinition,\n MessageDefinition,\n PublisherDefinition,\n QueueBindingDefinition,\n QueueEntry,\n TopicExchangeDefinition,\n} from \"../types.js\";\nimport { defineConsumer } from \"./consumer.js\";\nimport { definePublisherInternal } from \"./publisher.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Configuration for a command consumer.\n *\n * Commands are sent by one or more publishers to a single consumer (task queue pattern).\n * The consumer \"owns\" the queue, and publishers send commands to it.\n *\n * @template TMessage - The message definition\n * @template TExchange - The exchange definition\n * @template TRoutingKey - The routing key type (undefined for fanout)\n */\nexport type CommandConsumerConfig<\n TMessage extends MessageDefinition,\n TExchange extends ExchangeDefinition,\n TRoutingKey extends string | undefined = undefined,\n> = {\n /** Discriminator to identify this as a command consumer config */\n __brand: \"CommandConsumerConfig\";\n /** The consumer definition for processing commands */\n consumer: ConsumerDefinition<TMessage>;\n /** The binding connecting the queue to the exchange */\n binding: QueueBindingDefinition;\n /** The exchange that receives commands */\n exchange: TExchange;\n /** The message definition */\n message: TMessage;\n /** The routing key pattern for the binding */\n routingKey: TRoutingKey;\n};\n\n/**\n * Define a command consumer for receiving commands via fanout exchange.\n *\n * Commands are sent by publishers to a specific queue. The consumer \"owns\" the\n * queue and defines what commands it accepts.\n *\n * @param queue - The queue that will receive commands\n * @param exchange - The fanout exchange that routes commands\n * @param message - The message definition (schema and metadata)\n * @returns A command consumer configuration\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'fanout', { durable: true });\n * const taskMessage = defineMessage(z.object({ taskId: z.string() }));\n *\n * // Consumer owns the queue\n * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage);\n *\n * // Publishers send commands to it\n * const sendTask = defineCommandPublisher(executeTask);\n * ```\n */\nexport function defineCommandConsumer<TMessage extends MessageDefinition>(\n queue: QueueEntry,\n exchange: FanoutExchangeDefinition,\n message: TMessage,\n): CommandConsumerConfig<TMessage, FanoutExchangeDefinition, undefined>;\n\n/**\n * Define a command consumer for receiving commands via direct exchange.\n *\n * Commands are sent by publishers with a specific routing key that matches\n * the binding pattern.\n *\n * @param queue - The queue that will receive commands\n * @param exchange - The direct exchange that routes commands\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key\n * @param options.routingKey - The routing key for the binding\n * @param options.arguments - Additional AMQP arguments\n * @returns A command consumer configuration\n *\n * @example\n * ```typescript\n * const tasksExchange = defineExchange('tasks', 'direct', { durable: true });\n * const taskMessage = defineMessage(z.object({ taskId: z.string() }));\n *\n * const executeTask = defineCommandConsumer(taskQueue, tasksExchange, taskMessage, {\n * routingKey: 'task.execute',\n * });\n *\n * const sendTask = defineCommandPublisher(executeTask);\n * ```\n */\nexport function defineCommandConsumer<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n queue: QueueEntry,\n exchange: DirectExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: RoutingKey<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): CommandConsumerConfig<TMessage, DirectExchangeDefinition, TRoutingKey>;\n\n/**\n * Define a command consumer for receiving commands via topic exchange.\n *\n * The consumer binds with a routing key pattern (can use * and # wildcards).\n * Publishers then send commands with concrete routing keys that match the pattern.\n *\n * @param queue - The queue that will receive commands\n * @param exchange - The topic exchange that routes commands\n * @param message - The message definition (schema and metadata)\n * @param options - Configuration with required routing key pattern\n * @param options.routingKey - The routing key pattern for the binding\n * @param options.arguments - Additional AMQP arguments\n * @returns A command consumer configuration\n *\n * @example\n * ```typescript\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderMessage = defineMessage(z.object({ orderId: z.string() }));\n *\n * // Consumer uses pattern to receive multiple command types\n * const processOrder = defineCommandConsumer(orderQueue, ordersExchange, orderMessage, {\n * routingKey: 'order.*',\n * });\n *\n * // Publishers send with concrete keys\n * const createOrder = defineCommandPublisher(processOrder, {\n * routingKey: 'order.create',\n * });\n * const updateOrder = defineCommandPublisher(processOrder, {\n * routingKey: 'order.update',\n * });\n * ```\n */\nexport function defineCommandConsumer<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n queue: QueueEntry,\n exchange: TopicExchangeDefinition,\n message: TMessage,\n options: {\n routingKey: BindingPattern<TRoutingKey>;\n arguments?: Record<string, unknown>;\n },\n): CommandConsumerConfig<TMessage, TopicExchangeDefinition, TRoutingKey>;\n\n/**\n * Implementation of defineCommandConsumer.\n * @internal\n */\nexport function defineCommandConsumer<TMessage extends MessageDefinition>(\n queue: QueueEntry,\n exchange: ExchangeDefinition,\n message: TMessage,\n options?: {\n routingKey?: string;\n arguments?: Record<string, unknown>;\n },\n): CommandConsumerConfig<TMessage, ExchangeDefinition, string | undefined> {\n const consumer = defineConsumer(queue, message);\n const binding = defineQueueBindingInternal(queue, exchange, options);\n\n return {\n __brand: \"CommandConsumerConfig\",\n consumer,\n binding,\n exchange,\n message,\n routingKey: options?.routingKey,\n };\n}\n\n/**\n * Create a publisher that sends commands to a fanout exchange consumer.\n *\n * @param commandConsumer - The command consumer configuration\n * @returns A publisher definition\n *\n * @example\n * ```typescript\n * const executeTask = defineCommandConsumer(taskQueue, fanoutExchange, taskMessage);\n * const sendTask = defineCommandPublisher(executeTask);\n * ```\n */\nexport function defineCommandPublisher<TMessage extends MessageDefinition>(\n commandConsumer: CommandConsumerConfig<TMessage, FanoutExchangeDefinition, undefined>,\n): { message: TMessage; exchange: FanoutExchangeDefinition };\n\n/**\n * Create a publisher that sends commands to a direct exchange consumer.\n *\n * @param commandConsumer - The command consumer configuration\n * @returns A publisher definition\n */\nexport function defineCommandPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n>(\n commandConsumer: CommandConsumerConfig<TMessage, DirectExchangeDefinition, TRoutingKey>,\n): { message: TMessage; exchange: DirectExchangeDefinition; routingKey: string };\n\n/**\n * Create a publisher that sends commands to a topic exchange consumer.\n *\n * For topic exchanges where the consumer uses a pattern, the publisher can\n * optionally specify a concrete routing key that matches the pattern.\n *\n * @param commandConsumer - The command consumer configuration\n * @param options - Optional publisher configuration\n * @param options.routingKey - Override routing key (must match consumer's pattern)\n * @returns A publisher definition\n *\n * @example\n * ```typescript\n * // Consumer binds with pattern\n * const processOrder = defineCommandConsumer(orderQueue, topicExchange, orderMessage, {\n * routingKey: 'order.*',\n * });\n *\n * // Publisher uses concrete key matching the pattern\n * const createOrder = defineCommandPublisher(processOrder, {\n * routingKey: 'order.create',\n * });\n * ```\n */\nexport function defineCommandPublisher<\n TMessage extends MessageDefinition,\n TRoutingKey extends string,\n TPublisherRoutingKey extends string = TRoutingKey,\n>(\n commandConsumer: CommandConsumerConfig<TMessage, TopicExchangeDefinition, TRoutingKey>,\n options?: {\n routingKey?: RoutingKey<TPublisherRoutingKey>;\n },\n): { message: TMessage; exchange: TopicExchangeDefinition; routingKey: string };\n\n/**\n * Implementation of defineCommandPublisher.\n * @internal\n */\nexport function defineCommandPublisher<TMessage extends MessageDefinition>(\n commandConsumer: CommandConsumerConfig<TMessage, ExchangeDefinition, string | undefined>,\n options?: {\n routingKey?: string;\n },\n): PublisherDefinition<TMessage> {\n const { exchange, message, routingKey: consumerRoutingKey } = commandConsumer;\n\n // For topic exchanges, publisher can override the routing key\n const publisherRoutingKey = options?.routingKey ?? consumerRoutingKey;\n\n const publisherOptions: { routingKey?: string } = {};\n if (publisherRoutingKey !== undefined) {\n publisherOptions.routingKey = publisherRoutingKey;\n }\n\n return definePublisherInternal(exchange, message, publisherOptions);\n}\n\n/**\n * Type guard to check if a value is a CommandConsumerConfig.\n *\n * @param value - The value to check\n * @returns True if the value is a CommandConsumerConfig\n */\nexport function isCommandConsumerConfig(\n value: unknown,\n): value is CommandConsumerConfig<MessageDefinition, ExchangeDefinition, string | undefined> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"__brand\" in value &&\n value.__brand === \"CommandConsumerConfig\"\n );\n}\n","import type {\n BindingDefinition,\n ConsumerDefinition,\n ContractDefinition,\n ContractDefinitionInput,\n PublisherDefinition,\n QueueDefinition,\n} from \"../types.js\";\nimport { isEventConsumerResult, isEventPublisherConfig } from \"./event.js\";\nimport { definePublisherInternal } from \"./publisher.js\";\nimport { isCommandConsumerConfig } from \"./command.js\";\nimport { isQueueWithTtlBackoffInfrastructure } from \"./queue.js\";\n\n/**\n * Type utility to produce the output contract type from the input.\n * The output preserves the exact input type structure to maintain all specific\n * type information (routing keys, message types, etc.) while the runtime\n * transforms the values correctly.\n */\ntype ContractOutput<TContract extends ContractDefinitionInput> = TContract;\n\n/**\n * Define an AMQP contract.\n *\n * A contract is the central definition of your AMQP messaging topology. It brings together\n * all exchanges, queues, bindings, publishers, and consumers in a single, type-safe definition.\n *\n * The contract is used by both clients (for publishing) and workers (for consuming) to ensure\n * type safety throughout your messaging infrastructure. TypeScript will infer all message types\n * and publisher/consumer names from the contract.\n *\n * @param definition - The contract definition containing all AMQP resources\n * @param definition.exchanges - Named exchange definitions\n * @param definition.queues - Named queue definitions\n * @param definition.bindings - Named binding definitions (queue-to-exchange or exchange-to-exchange)\n * @param definition.publishers - Named publisher definitions for sending messages\n * @param definition.consumers - Named consumer definitions for receiving messages\n * @returns The same contract definition with full type inference\n *\n * @example\n * ```typescript\n * import {\n * defineContract,\n * defineExchange,\n * defineQueue,\n * defineQueueBinding,\n * definePublisher,\n * defineConsumer,\n * defineMessage,\n * } from '@amqp-contract/contract';\n * import { z } from 'zod';\n *\n * // Define resources\n * const ordersExchange = defineExchange('orders', 'topic', { durable: true });\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const orderMessage = defineMessage(\n * z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * })\n * );\n *\n * // Compose contract\n * export const contract = defineContract({\n * exchanges: {\n * orders: ordersExchange,\n * },\n * queues: {\n * orderProcessing: orderQueue,\n * },\n * bindings: {\n * orderBinding: defineQueueBinding(orderQueue, ordersExchange, {\n * routingKey: 'order.created',\n * }),\n * },\n * publishers: {\n * orderCreated: definePublisher(ordersExchange, orderMessage, {\n * routingKey: 'order.created',\n * }),\n * },\n * consumers: {\n * processOrder: defineConsumer(orderQueue, orderMessage),\n * },\n * });\n *\n * // TypeScript now knows:\n * // - client.publish('orderCreated', { orderId: string, amount: number })\n * // - handler: async (message: { orderId: string, amount: number }) => void\n * ```\n */\nexport function defineContract<TContract extends ContractDefinitionInput>(\n definition: TContract,\n): ContractOutput<TContract> {\n // Exclude publishers and consumers from spread since they may contain config entries\n const { publishers: inputPublishers, consumers: inputConsumers, ...rest } = definition;\n const result: ContractDefinition = rest as ContractDefinition;\n\n // Process queues to extract TTL-backoff infrastructure\n if (definition.queues && Object.keys(definition.queues).length > 0) {\n const expandedQueues: Record<string, QueueDefinition> = {};\n const queueBindings: Record<string, BindingDefinition> = {};\n\n for (const [name, entry] of Object.entries(definition.queues)) {\n if (isQueueWithTtlBackoffInfrastructure(entry)) {\n expandedQueues[name] = entry.queue;\n expandedQueues[`${name}Wait`] = entry.waitQueue;\n queueBindings[`${name}WaitBinding`] = entry.waitQueueBinding;\n queueBindings[`${name}RetryBinding`] = entry.mainQueueRetryBinding;\n } else {\n expandedQueues[name] = entry as QueueDefinition;\n }\n }\n\n result.queues = expandedQueues;\n\n if (Object.keys(queueBindings).length > 0) {\n result.bindings = { ...result.bindings, ...queueBindings };\n }\n }\n\n // Process publishers section - extract EventPublisherConfig entries\n if (inputPublishers && Object.keys(inputPublishers).length > 0) {\n const processedPublishers: Record<string, PublisherDefinition> = {};\n\n for (const [name, entry] of Object.entries(inputPublishers)) {\n if (isEventPublisherConfig(entry)) {\n // EventPublisherConfig: extract to publisher definition\n const publisherOptions: { routingKey?: string } = {};\n if (entry.routingKey !== undefined) {\n publisherOptions.routingKey = entry.routingKey;\n }\n processedPublishers[name] = definePublisherInternal(\n entry.exchange,\n entry.message,\n publisherOptions,\n );\n } else {\n // Plain PublisherDefinition\n processedPublishers[name] = entry as PublisherDefinition;\n }\n }\n\n result.publishers = processedPublishers;\n }\n\n // Process consumers section - extract EventConsumerResult and CommandConsumerConfig entries\n if (inputConsumers && Object.keys(inputConsumers).length > 0) {\n const processedConsumers: Record<string, ConsumerDefinition> = {};\n const consumerBindings: Record<string, BindingDefinition> = {};\n\n for (const [name, entry] of Object.entries(inputConsumers)) {\n if (isEventConsumerResult(entry)) {\n // EventConsumerResult: extract consumer and binding\n processedConsumers[name] = entry.consumer;\n consumerBindings[`${name}Binding`] = entry.binding;\n } else if (isCommandConsumerConfig(entry)) {\n // CommandConsumerConfig: extract consumer and binding\n processedConsumers[name] = entry.consumer;\n consumerBindings[`${name}Binding`] = entry.binding;\n } else {\n // Plain ConsumerDefinition\n processedConsumers[name] = entry as ConsumerDefinition;\n }\n }\n\n result.consumers = processedConsumers;\n\n if (Object.keys(consumerBindings).length > 0) {\n result.bindings = { ...result.bindings, ...consumerBindings };\n }\n }\n\n return result as ContractOutput<TContract>;\n}\n","import type { QueueBindingDefinition, QueueDefinition, QueueEntry } from \"../types.js\";\nimport { defineQueue, extractQueue } from \"./queue.js\";\nimport { defineQueueBindingInternal } from \"./binding.js\";\n\n/**\n * Result type for TTL-backoff retry infrastructure builder.\n *\n * Contains the wait queue and bindings needed for TTL-backoff retry.\n */\nexport type TtlBackoffRetryInfrastructure = {\n /**\n * The wait queue for holding messages during backoff delay.\n * This is a classic queue with a dead letter exchange pointing back to the main queue.\n */\n waitQueue: QueueDefinition;\n /**\n * Binding that routes failed messages to the wait queue.\n */\n waitQueueBinding: QueueBindingDefinition;\n /**\n * Binding that routes retried messages back to the main queue.\n */\n mainQueueRetryBinding: QueueBindingDefinition;\n};\n\n/**\n * Create TTL-backoff retry infrastructure for a queue.\n *\n * This builder helper generates the wait queue and bindings needed for TTL-backoff retry.\n * The generated infrastructure can be spread into a contract definition.\n *\n * TTL-backoff retry works by:\n * 1. Failed messages are sent to the DLX with routing key `{queueName}-wait`\n * 2. The wait queue receives these messages and holds them for a TTL period\n * 3. After TTL expires, messages are dead-lettered back to the DLX with routing key `{queueName}`\n * 4. The main queue receives the retried message via its binding to the DLX\n *\n * @param queue - The main queue definition (must have deadLetter configured)\n * @param options - Optional configuration for the wait queue\n * @param options.waitQueueDurable - Whether the wait queue should be durable (default: same as main queue)\n * @returns TTL-backoff retry infrastructure containing wait queue and bindings\n * @throws {Error} If the queue does not have a dead letter exchange configured\n *\n * @example\n * ```typescript\n * const dlx = defineExchange('orders-dlx', 'direct', { durable: true });\n * const orderQueue = defineQueue('order-processing', {\n * type: 'quorum',\n * deadLetter: { exchange: dlx },\n * retry: {\n * mode: 'ttl-backoff',\n * maxRetries: 5,\n * initialDelayMs: 1000,\n * },\n * });\n *\n * // Generate TTL-backoff infrastructure\n * const retryInfra = defineTtlBackoffRetryInfrastructure(orderQueue);\n *\n * // Spread into contract\n * const contract = defineContract({\n * exchanges: { dlx },\n * queues: {\n * orderProcessing: orderQueue,\n * orderProcessingWait: retryInfra.waitQueue,\n * },\n * bindings: {\n * ...// your other bindings\n * orderWaitBinding: retryInfra.waitQueueBinding,\n * orderRetryBinding: retryInfra.mainQueueRetryBinding,\n * },\n * // ... publishers and consumers\n * });\n * ```\n */\nexport function defineTtlBackoffRetryInfrastructure(\n queueEntry: QueueEntry,\n options?: {\n waitQueueDurable?: boolean;\n },\n): TtlBackoffRetryInfrastructure {\n const queue = extractQueue(queueEntry);\n if (!queue.deadLetter) {\n throw new Error(\n `Queue \"${queue.name}\" does not have a dead letter exchange configured. ` +\n `TTL-backoff retry requires deadLetter to be set on the queue.`,\n );\n }\n\n const dlx = queue.deadLetter.exchange;\n const waitQueueName = `${queue.name}-wait`;\n\n // Create the wait queue - quorum for better durability\n // Wait queue uses default TTL-backoff retry (infrastructure queue, not directly consumed)\n const waitQueue = defineQueue(waitQueueName, {\n type: \"quorum\",\n durable: options?.waitQueueDurable ?? queue.durable ?? true,\n deadLetter: {\n exchange: dlx,\n routingKey: queue.name, // Routes back to main queue after TTL\n },\n }) as QueueDefinition;\n\n // Create binding for wait queue to receive failed messages\n const waitQueueBinding = defineQueueBindingInternal(waitQueue, dlx, {\n routingKey: waitQueueName,\n });\n\n // Create binding for main queue to receive retried messages\n const mainQueueRetryBinding = defineQueueBindingInternal(queue, dlx, {\n routingKey: queue.name,\n });\n\n return {\n waitQueue,\n waitQueueBinding,\n mainQueueRetryBinding,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;AA0GA,SAAgB,eACd,MACA,MACA,SACoB;AACpB,QAAO;EACL;EACA;EACA,GAAG;EACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC7EH,SAAgB,cAId,SACA,SAKuC;AACvC,QAAO;EACL;EACA,GAAG;EACJ;;;;;;;;;;ACnCH,SAASA,sCACP,OAC4C;AAC5C,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,MAAM,YAAY;;;;;;;AAStB,SAAS,qBAAqB,OAAoC;AAChE,KAAIA,sCAAoC,MAAM,CAC5C,QAAO,MAAM;AAEf,QAAO;;;;;;;;;;;;;AA4FT,SAAgB,mBACd,OACA,UACA,SAIwB;CAExB,MAAM,WAAW,qBAAqB,MAAM;AAE5C,KAAI,SAAS,SAAS,SACpB,QAAO;EACL,MAAM;EACN,OAAO;EACP;EACA,GAAI,SAAS,aAAa,EAAE,WAAW,QAAQ,WAAW;EAC3D;AAGH,QAAO;EACL,MAAM;EACN,OAAO;EACP;EACA,YAAY,SAAS;EACrB,GAAI,SAAS,aAAa,EAAE,WAAW,QAAQ,WAAW;EAC3D;;;;;;;AAQH,SAAgB,2BACd,OACA,UACA,SAIwB;AACxB,KAAI,SAAS,SAAS,SACpB,QAAO,mBAAmB,OAAO,UAAU,QAAQ;AAErD,QAAO,mBAAmB,OAAO,UAAU,QAAkC;;;;;;;;;;;;;AAmF/E,SAAgB,sBACd,aACA,QACA,SAI2B;AAC3B,KAAI,OAAO,SAAS,SAClB,QAAO;EACL,MAAM;EACN;EACA;EACA,GAAI,SAAS,aAAa,EAAE,WAAW,QAAQ,WAAW;EAC3D;AAGH,QAAO;EACL,MAAM;EACN;EACA;EACA,YAAY,SAAS,cAAc;EACnC,GAAI,SAAS,aAAa,EAAE,WAAW,QAAQ,WAAW;EAC3D;;;;;;;;;ACpQH,SAAgB,yBACd,SACgC;AAChC,QAAO;EACL,MAAM;EACN,YAAY,SAAS,cAAc;EACnC,gBAAgB,SAAS,kBAAkB;EAC3C,YAAY,SAAS,cAAc;EACnC,mBAAmB,SAAS,qBAAqB;EACjD,QAAQ,SAAS,UAAU;EAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCH,SAAgB,oCACd,OAC4C;AAC5C,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDtB,SAAgB,aAAa,OAAoC;AAC/D,KAAI,oCAAoC,MAAM,CAC5C,QAAO,MAAM;AAEf,QAAO;;;;;;AAOT,SAAS,iCACP,OACmC;AACnC,KAAI,CAAC,MAAM,WACT,OAAM,IAAI,MACR,UAAU,MAAM,KAAK,kHAEtB;CAGH,MAAM,MAAM,MAAM,WAAW;CAC7B,MAAM,gBAAgB,GAAG,MAAM,KAAK;CAIpC,MAAM,YAAmC;EACvC,MAAM;EACN,MAAM;EACN,SAAS,MAAM,WAAW;EAC1B,YAAY;GACV,UAAU;GACV,YAAY,MAAM;GACnB;EACD,OAAO,yBAAyB,OAAU;EAC3C;AAYD,QAAO;EACL,SAAS;EACT;EACA;EACA,kBAbuB,2BAA2B,WAAW,KAAK,EAClE,YAAY,eACb,CAAC;EAYA,uBAT4B,2BAA2B,OAAO,KAAK,EACnE,YAAY,MAAM,MACnB,CAAC;EAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiEH,SAAgB,YACd,MACA,SACqD;CACrD,MAAM,OAAO,WAAW,EAAE;CAC1B,MAAM,OAAO,KAAK,QAAQ;CAG1B,MAAM,YAMF,EAAE,MAAM;AAEZ,KAAI,KAAK,YAAY,OACnB,WAAU,UAAU,KAAK;AAG3B,KAAI,KAAK,eAAe,OACtB,WAAU,aAAa,KAAK;AAG9B,KAAI,KAAK,eAAe,OACtB,WAAU,aAAa,KAAK;AAG9B,KAAI,KAAK,cAAc,OACrB,WAAU,YAAY,KAAK;AAI7B,KAAI,SAAS,UAAU;EACrB,MAAM,aAAa;EACnB,MAAM,aAAa,WAAW,SAAS,EAAE,MAAM,eAAwB;AAGvE,MAAI,WAAW,SAAS,iBACtB;OAAI,WAAW,kBAAkB,OAC/B,OAAM,IAAI,MACR,UAAU,KAAK,4HAEhB;;EAKL,MAAMC,UACJ,WAAW,SAAS,kBAAkB,aAAa,yBAAyB,WAAW;EAEzF,MAAMC,oBAAyC;GAC7C,GAAG;GACH,MAAM;GACN;GACD;AAGD,MAAI,WAAW,kBAAkB,QAAW;AAC1C,OAAI,WAAW,gBAAgB,KAAK,CAAC,OAAO,UAAU,WAAW,cAAc,CAC7E,OAAM,IAAI,MACR,0BAA0B,WAAW,cAAc,+BACpD;AAEH,qBAAgB,gBAAgB,WAAW;;AAI7C,MAAID,QAAM,SAAS,iBAAiBC,kBAAgB,WAClD,QAAO,iCAAiCA,kBAAgB;AAG1D,SAAOA;;CAIT,MAAM,cAAc;AAGpB,KAAK,YAAY,OAAyC,SAAS,gBACjE,OAAM,IAAI,MACR,UAAU,KAAK,sHAEhB;CAIH,MAAM,QAAQ,yBAAyB,YAAY,MAAM;CAEzD,MAAM,kBAA0C;EAC9C,GAAG;EACH,MAAM;EACN;EACD;AAGD,KAAI,YAAY,cAAc,OAC5B,iBAAgB,YAAY,YAAY;AAI1C,KAAI,YAAY,gBAAgB,QAAW;AACzC,MAAI,YAAY,cAAc,KAAK,YAAY,cAAc,IAC3D,OAAM,IAAI,MACR,wBAAwB,YAAY,YAAY,uDACjD;AAEH,kBAAgB,YAAY;GAC1B,GAAG,gBAAgB;GACnB,kBAAkB,YAAY;GAC/B;;AAIH,KAAI,MAAM,SAAS,iBAAiB,gBAAgB,WAClD,QAAO,iCAAiC,gBAAgB;AAG1D,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgFT,SAAgB,kBACd,MACA,SACuB;CACvB,MAAM,EACJ,oBACA,sBACA,eACA,YACA,WAAW,SACT;CAMJ,MAAM,eAAmC;EACvC,MAAM;EACN,YANmC,uBACjC;GAAE,UAAU;GAAoB,YAAY;GAAsB,GAClE,EAAE,UAAU,oBAAoB;EAKlC;EACA,OAAO,EAAE,MAAM,iBAAiB;EACjC;AAED,KAAI,eAAe,OAAW,cAAa,aAAa;AACxD,KAAI,SAAS,OAAW,cAAa,YAAY;AAEjD,QAAO,YAAY,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgHxC,SAAgB,sBACd,MACA,SACmC;CACnC,MAAM,EACJ,oBACA,sBACA,YACA,gBACA,YACA,mBACA,QACA,YACA,WAAW,SACT;CAEJ,MAAM,aAA+B,uBACjC;EAAE,UAAU;EAAoB,YAAY;EAAsB,GAClE,EAAE,UAAU,oBAAoB;CAGpC,MAAM,eAAuC,EAAE,MAAM,eAAe;AACpE,KAAI,eAAe,OAAW,cAAa,aAAa;AACxD,KAAI,mBAAmB,OAAW,cAAa,iBAAiB;AAChE,KAAI,eAAe,OAAW,cAAa,aAAa;AACxD,KAAI,sBAAsB,OAAW,cAAa,oBAAoB;AACtE,KAAI,WAAW,OAAW,cAAa,SAAS;CAEhD,MAAM,eAAmC;EACvC,MAAM;EACN;EACA,OAAO;EACR;AAED,KAAI,eAAe,OAAW,cAAa,aAAa;AACxD,KAAI,SAAS,OAAW,cAAa,YAAY;AAMjD,QAJe,YAAY,MAAM,aAAa;;;;;;;;;;;;;;;;AC5dhD,SAAgB,gBACd,UACA,SACA,SAC+B;AAC/B,KAAI,SAAS,SAAS,SACpB,QAAO;EACL;EACA;EACD;AAGH,QAAO;EACL;EACA;EACA,YAAY,SAAS,cAAc;EACpC;;;;;;;AAQH,SAAgB,wBACd,UACA,SACA,SAI+B;AAE/B,KAAI,SAAS,SAAS,SACpB,QAAO,gBAAgB,UAAU,SAAS,QAAQ;AAEpD,QAAO,gBAAgB,UAAU,SAAS,QAAkC;;;;;;;;ACnK9E,SAAS,2BAA2B,OAAwD;AAC1F,QAAO,aAAa,SAAS,MAAM,YAAY;;;;;AAMjD,SAAS,6BAA6B,OAA0D;AAC9F,QAAO,aAAa,SAAS,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCjD,SAAgB,gBAAgB,OAA0C;AACxE,KAAI,2BAA2B,MAAM,IAAI,6BAA6B,MAAM,CAC1E,QAAO,MAAM;AAGf,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DT,SAAgB,eACd,OACA,SACA,SAC8B;AAC9B,QAAO;EACL,OAAO,aAAa,MAAM;EAC1B;EACA,GAAG;EACJ;;;;;;;;;ACiDH,SAAgB,qBACd,UACA,SACA,SAIwE;CACxE,MAAM,SAAiF;EACrF,SAAS;EACT;EACA;EACA,YAAY,SAAS;EACtB;AAED,KAAI,SAAS,cAAc,OACzB,QAAO,YAAY,QAAQ;AAG7B,QAAO;;;;;;AAqFT,SAAgB,oBACd,gBACA,OACA,SAI+B;CAC/B,MAAM,EAAE,UAAU,SAAS,YAAY,wBAAwB;CAG/D,MAAM,oBAAoB,SAAS,cAAc;CAEjD,MAAM,iBAA+E,EAAE;AACvF,KAAI,sBAAsB,OACxB,gBAAe,aAAa;CAE9B,MAAM,mBAAmB,SAAS,aAAa,eAAe;AAC9D,KAAI,qBAAqB,OACvB,gBAAe,YAAY;CAG7B,MAAM,UAAU,2BAA2B,OAAO,UAAU,eAAe;AAG3E,QAAO;EACL,SAAS;EACT,UAJe,eAAe,OAAO,QAAQ;EAK7C;EACD;;;;;;;;AASH,SAAgB,uBACd,OAC0F;AAC1F,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,MAAM,YAAY;;;;;;;;AAUtB,SAAgB,sBACd,OACiD;AACjD,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,MAAM,YAAY;;;;;;;;;ACrLtB,SAAgB,sBACd,OACA,UACA,SACA,SAIyE;AAIzE,QAAO;EACL,SAAS;EACT,UALe,eAAe,OAAO,QAAQ;EAM7C,SALc,2BAA2B,OAAO,UAAU,QAAQ;EAMlE;EACA;EACA,YAAY,SAAS;EACtB;;;;;;AAuEH,SAAgB,uBACd,iBACA,SAG+B;CAC/B,MAAM,EAAE,UAAU,SAAS,YAAY,uBAAuB;CAG9D,MAAM,sBAAsB,SAAS,cAAc;CAEnD,MAAM,mBAA4C,EAAE;AACpD,KAAI,wBAAwB,OAC1B,kBAAiB,aAAa;AAGhC,QAAO,wBAAwB,UAAU,SAAS,iBAAiB;;;;;;;;AASrE,SAAgB,wBACd,OAC2F;AAC3F,QACE,OAAO,UAAU,YACjB,UAAU,QACV,aAAa,SACb,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACnMtB,SAAgB,eACd,YAC2B;CAE3B,MAAM,EAAE,YAAY,iBAAiB,WAAW,gBAAgB,GAAG,SAAS;CAC5E,MAAM,SAA6B;AAGnC,KAAI,WAAW,UAAU,OAAO,KAAK,WAAW,OAAO,CAAC,SAAS,GAAG;EAClE,MAAM,iBAAkD,EAAE;EAC1D,MAAM,gBAAmD,EAAE;AAE3D,OAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,WAAW,OAAO,CAC3D,KAAI,oCAAoC,MAAM,EAAE;AAC9C,kBAAe,QAAQ,MAAM;AAC7B,kBAAe,GAAG,KAAK,SAAS,MAAM;AACtC,iBAAc,GAAG,KAAK,gBAAgB,MAAM;AAC5C,iBAAc,GAAG,KAAK,iBAAiB,MAAM;QAE7C,gBAAe,QAAQ;AAI3B,SAAO,SAAS;AAEhB,MAAI,OAAO,KAAK,cAAc,CAAC,SAAS,EACtC,QAAO,WAAW;GAAE,GAAG,OAAO;GAAU,GAAG;GAAe;;AAK9D,KAAI,mBAAmB,OAAO,KAAK,gBAAgB,CAAC,SAAS,GAAG;EAC9D,MAAM,sBAA2D,EAAE;AAEnE,OAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,gBAAgB,CACzD,KAAI,uBAAuB,MAAM,EAAE;GAEjC,MAAM,mBAA4C,EAAE;AACpD,OAAI,MAAM,eAAe,OACvB,kBAAiB,aAAa,MAAM;AAEtC,uBAAoB,QAAQ,wBAC1B,MAAM,UACN,MAAM,SACN,iBACD;QAGD,qBAAoB,QAAQ;AAIhC,SAAO,aAAa;;AAItB,KAAI,kBAAkB,OAAO,KAAK,eAAe,CAAC,SAAS,GAAG;EAC5D,MAAM,qBAAyD,EAAE;EACjE,MAAM,mBAAsD,EAAE;AAE9D,OAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,eAAe,CACxD,KAAI,sBAAsB,MAAM,EAAE;AAEhC,sBAAmB,QAAQ,MAAM;AACjC,oBAAiB,GAAG,KAAK,YAAY,MAAM;aAClC,wBAAwB,MAAM,EAAE;AAEzC,sBAAmB,QAAQ,MAAM;AACjC,oBAAiB,GAAG,KAAK,YAAY,MAAM;QAG3C,oBAAmB,QAAQ;AAI/B,SAAO,YAAY;AAEnB,MAAI,OAAO,KAAK,iBAAiB,CAAC,SAAS,EACzC,QAAO,WAAW;GAAE,GAAG,OAAO;GAAU,GAAG;GAAkB;;AAIjE,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACjGT,SAAgB,oCACd,YACA,SAG+B;CAC/B,MAAM,QAAQ,aAAa,WAAW;AACtC,KAAI,CAAC,MAAM,WACT,OAAM,IAAI,MACR,UAAU,MAAM,KAAK,kHAEtB;CAGH,MAAM,MAAM,MAAM,WAAW;CAC7B,MAAM,gBAAgB,GAAG,MAAM,KAAK;CAIpC,MAAM,YAAY,YAAY,eAAe;EAC3C,MAAM;EACN,SAAS,SAAS,oBAAoB,MAAM,WAAW;EACvD,YAAY;GACV,UAAU;GACV,YAAY,MAAM;GACnB;EACF,CAAC;AAYF,QAAO;EACL;EACA,kBAXuB,2BAA2B,WAAW,KAAK,EAClE,YAAY,eACb,CAAC;EAUA,uBAP4B,2BAA2B,OAAO,KAAK,EACnE,YAAY,MAAM,MACnB,CAAC;EAMD"}
|