import { AsyncResult, Result } from "unthrown"; import { AmqpConnectionManager, AmqpConnectionManagerOptions, ConnectionUrl, CreateChannelOpts } from "amqp-connection-manager"; import { ContractDefinition } from "@amqp-contract/contract"; import { Attributes, Counter, Histogram, Span, Tracer } from "@opentelemetry/api"; import { Channel, ConsumeMessage, Options } from "amqplib"; //#region src/errors.d.ts declare const TechnicalError_base: import("unthrown").TaggedErrorConstructor<"@amqp-contract/TechnicalError">; /** * Error for technical/runtime failures that cannot be prevented by TypeScript. * * This includes AMQP connection failures, channel issues, validation failures, * and other runtime errors. This error is shared across core, worker, and client packages. * * Built on unthrown's {@link TaggedError}, so it carries a `_tag` of * `"@amqp-contract/TechnicalError"` for exhaustive dispatch via `matchTags`. The * tag is namespaced to avoid colliding with other libraries' tags in a shared * `matchTags`; the human-facing `Error.name` is kept bare (`"TechnicalError"`). * Remains a real `Error` (and a *modeled* error — it lives in the `E` channel of * a `Result`, never the `Defect` channel). */ declare class TechnicalError extends TechnicalError_base<{ cause?: unknown; }> { constructor(message: string, cause?: unknown); } declare const MessageValidationError_base: import("unthrown").TaggedErrorConstructor<"@amqp-contract/MessageValidationError">; /** * Error thrown when message validation fails (payload or headers). * * Used by both the client (publish-time payload validation) and the worker * (consume-time payload and headers validation). Carries a `_tag` of * `"@amqp-contract/MessageValidationError"` (namespaced to avoid collisions); * the `Error.name` is kept bare (`"MessageValidationError"`). * * @param source - The name of the publisher or consumer that triggered the validation * @param issues - The validation issues from the Standard Schema validation */ declare class MessageValidationError extends MessageValidationError_base<{ source: string; issues: unknown; }> { constructor(source: string, issues: unknown); } /** * AMQP message header carrying the error code of a typed RPC error reply. * * A reply message with this header is an error reply: its body is * `{ message, data }` where `data` conforms to the error's declared schema in * the RPC's `errors` map. A reply without it is a regular success reply whose * body is the response payload — so success replies are wire-compatible with * contracts that declare no errors. */ declare const RPC_ERROR_CODE_HEADER = "x-amqp-contract-error-code"; declare const RpcError_base: import("unthrown").TaggedErrorConstructor<"@amqp-contract/RpcError">; /** * A typed, contract-declared RPC error — the business-failure channel of an * RPC, as opposed to the transport failures modeled by {@link TechnicalError}. * * Declared per-RPC via `defineRpc(queue, { request, response, errors })`, * where each error code maps to a message definition validating the error's * `data` payload. A worker handler surfaces one by returning * `Err(rpcError(code, data))`; the worker validates `data` against the * declared schema, publishes an error reply, and acks the request (business * errors are not retried). The caller's `client.call(...)` resolves to * `Err(RpcError)` with `data` re-validated on arrival. * * Carries a `_tag` of `"@amqp-contract/RpcError"` for exhaustive dispatch via * `matchTags`; the `Error.name` is kept bare (`"RpcError"`). Discriminate * between codes on the `code` property. */ declare class RpcError extends RpcError_base<{ code: string; data: unknown; }> { readonly code: TCode; readonly data: TData; constructor(code: TCode, data: TData, message?: string); } /** * Type guard to check if an error is an {@link RpcError}. * * Narrowing to a specific code (and thus a typed `data`) is done on the * `code` property after the guard, or via `matchTags` on the `_tag`. */ declare function isRpcError(error: unknown): error is RpcError; /** * Create an {@link RpcError} with less verbosity. * * The code/data pair must match one of the entries declared in the RPC's * `errors` map — the handler's return type enforces this at compile time, and * the worker validates `data` against the declared schema at runtime before * replying. * * @param code - The error code, as declared in the RPC's `errors` map * @param data - The error data, validated against the declared schema * @param message - Optional human-readable message (defaults to a generic one) * * @example * ```typescript * import { rpcError } from '@amqp-contract/worker'; * import { ErrAsync } from 'unthrown'; * * const handler = ({ payload }) => { * if (!orders.has(payload.orderId)) { * return ErrAsync(rpcError('ORDER_NOT_FOUND', { orderId: payload.orderId })); * } * // ... * }; * ``` */ declare function rpcError(code: TCode, data: TData, message?: string): RpcError; //#endregion //#region src/amqp-client.d.ts /** * Default time `waitForConnect` will wait for the broker before erroring out. * Defaulting to a finite value (rather than waiting forever) means a fail-fast * developer experience: a misconfigured URL, a down broker, or wrong * credentials surface as an `err` within 30 seconds. Pass `null` * explicitly to disable the timeout — `Infinity` and other non-finite values * are also coerced to "no timeout" because Node's `setTimeout` clamps large * delays to ~24.8 days and silently fires near-immediately on `Infinity`. */ declare const DEFAULT_CONNECT_TIMEOUT_MS = 30000; /** * Options for creating an AMQP client. * * @property urls - AMQP broker URL(s). Multiple URLs provide failover support. * @property connectionOptions - Optional connection configuration (heartbeat, reconnect settings, etc.). * @property channelOptions - Optional channel configuration options. * @property connectTimeoutMs - Maximum time in ms to wait for the channel to * become ready in `waitForConnect`. Defaults to {@link DEFAULT_CONNECT_TIMEOUT_MS}. * Pass `null` to disable the timeout entirely (amqp-connection-manager will * retry indefinitely). */ type AmqpClientOptions = { urls: ConnectionUrl[]; connectionOptions?: AmqpConnectionManagerOptions | undefined; channelOptions?: Partial | undefined; connectTimeoutMs?: number | null | undefined; }; /** * Callback type for consuming messages. */ type ConsumeCallback = (msg: ConsumeMessage | null) => void | Promise; /** * Publish options for `AmqpClient.publish` / `AmqpClient.sendToQueue`. * * Currently a re-export of amqplib's `Options.Publish`. A previous version of * this type also exposed a `timeout` field, but that field never had a * meaningful AMQP-level effect in this codebase and has been removed to avoid * suggesting behaviour we do not provide. (`amqp-connection-manager`'s own * `publishTimeout` channel option is unrelated and is configured at channel * creation, not per-publish.) */ type PublishOptions = Options.Publish; /** * Consume options that extend amqplib's `Options.Consume` with an optional * per-consumer prefetch count. * * `prefetch` is intercepted by {@link AmqpClient.consume}: it is stripped from * the options handed to the underlying `channelWrapper.consume(...)` call * (since amqplib's `Options.Consume` does not include it) and applied via * `channel.prefetch(count, false)` registered through `addSetup` *before* the * consume so the value is in effect when the consumer starts and is reapplied * automatically on channel reconnect. */ type ConsumerOptions = Options.Consume & { /** Per-consumer prefetch count. Applied before `channel.consume(...)`. */prefetch?: number; }; /** * AMQP client that manages connections and channels with automatic topology setup. * * This class handles: * - Connection management with automatic reconnection via amqp-connection-manager * - Connection pooling and sharing across instances with the same URLs * - Automatic AMQP topology setup (exchanges, queues, bindings) from contract * - Channel creation with JSON serialization enabled by default * * All operations return `AsyncResult` for consistent error handling. * * @example * ```typescript * const client = new AmqpClient(contract, { * urls: ['amqp://localhost'], * connectionOptions: { heartbeatIntervalInSeconds: 30 } * }); * * // Wait for connection (AsyncResult is thenable) * await client.waitForConnect(); * * // Publish a message * const result = await client.publish('exchange', 'routingKey', { data: 'value' }); * * // Close when done * await client.close(); * ``` */ declare class AmqpClient { private readonly contract; private readonly connection; private readonly channelWrapper; private readonly urls; private readonly connectionOptions?; /** Resolved timeout in ms; `null` means "wait forever". */ private readonly connectTimeoutMs; /** * Per-consumer prefetch setup functions registered via `addSetup` so they * can be removed in {@link cancel} once the consumer is gone — otherwise * the channel wrapper would replay the cancelled consumer's QoS on every * reconnect and silently apply it to subsequent consumers. * * @internal */ private readonly prefetchSetups; /** * Create a new AMQP client instance. * * The client will automatically: * - Get or create a shared connection using the singleton pattern * - Set up AMQP topology (exchanges, queues, bindings) from the contract * - Create a channel with JSON serialization enabled * * @param contract - The contract definition specifying the AMQP topology * @param options - Client configuration options */ constructor(contract: ContractDefinition, options: AmqpClientOptions); /** * Get the underlying connection manager * * This method exposes the AmqpConnectionManager instance that this client uses. * The connection is automatically shared across all AmqpClient instances that * use the same URLs and connection options. * * @returns The AmqpConnectionManager instance used by this client */ getConnection(): AmqpConnectionManager; /** * Wait for the channel to be connected and ready. * * If `connectTimeoutMs` was provided in the constructor options, the returned * AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses. * Without a timeout, this waits forever — amqp-connection-manager retries * connections indefinitely and never errors on its own. * * NOTE: When using `AmqpClient` directly (not via `TypedAmqpClient` / * `TypedAmqpWorker`), the constructor has already incremented the pooled * connection's reference count. Callers must invoke `close()` on the error * path to release the connection — `waitForConnect` does not do this * automatically. The typed factories handle this cleanup for you. */ waitForConnect(): AsyncResult; /** * Publish a message to an exchange. * * @returns AsyncResult resolving to `true` if the message was sent, `false` if the channel buffer is full. */ publish(exchange: string, routingKey: string, content: Buffer | unknown, options?: PublishOptions): AsyncResult; /** * Publish a message directly to a queue. * * @returns AsyncResult resolving to `true` if the message was sent, `false` if the channel buffer is full. */ sendToQueue(queue: string, content: Buffer | unknown, options?: PublishOptions): AsyncResult; /** * Start consuming messages from a queue. * * If `options.prefetch` is set, a per-consumer prefetch count is applied via * `channel.prefetch(count, false)` registered as a setup function on the * channel wrapper *before* the underlying `consume` call. Registering it via * `addSetup` ensures the prefetch is reapplied automatically on channel * reconnect; using `global=false` scopes it to subsequent consumers on the * channel (RabbitMQ semantics — opposite of intuition: `false` is per- * consumer, `true` is channel-wide). * * `prefetch` is stripped from the options handed to `channelWrapper.consume` * because it is not a valid `amqplib` `Options.Consume` field — leaving it * in would just travel as a no-op key-value pair on the consume frame. * * @returns AsyncResult resolving to the consumer tag. */ consume(queue: string, callback: ConsumeCallback, options?: ConsumerOptions): AsyncResult; /** * Cancel a consumer by its consumer tag. */ cancel(consumerTag: string): AsyncResult; /** * Acknowledge a message. * * @param msg - The message to acknowledge * @param allUpTo - If true, acknowledge all messages up to and including this one */ ack(msg: ConsumeMessage, allUpTo?: boolean): void; /** * Negative acknowledge a message. * * @param msg - The message to nack * @param allUpTo - If true, nack all messages up to and including this one * @param requeue - If true, requeue the message(s) */ nack(msg: ConsumeMessage, allUpTo?: boolean, requeue?: boolean): void; /** * Add a setup function to be called when the channel is created or reconnected. * * This is useful for setting up channel-level configuration like prefetch. * * @param setup - The setup function to add */ addSetup(setup: (channel: Channel) => void | Promise): void; /** * Register an event listener on the channel wrapper. * * Available events: * - 'connect': Emitted when the channel is (re)connected * - 'close': Emitted when the channel is closed * - 'error': Emitted when an error occurs * * @param event - The event name * @param listener - The event listener */ on(event: string, listener: (...args: unknown[]) => void): void; /** * Close the channel and release the connection reference. * * This will: * - Close the channel wrapper * - Decrease the reference count on the shared connection * - Close the connection if this was the last client using it * * Both steps run regardless of each other's outcome; if both fail, the * errors are wrapped in an AggregateError. */ close(): AsyncResult; /** * Reset connection singleton cache (for testing only) * @internal */ static _resetConnectionCacheForTesting(): Promise; } //#endregion //#region src/connection-manager.d.ts /** * Number of active pooled connections. Test-only helper — exposed in lieu of * the underlying singleton, which is intentionally not part of the public API * (mutating it from outside the library can break in-flight clients sharing a * connection). * * @internal */ declare function _internal_getConnectionCount(): number; /** @deprecated Renamed to {@link _internal_getConnectionCount} per the org `_internal_` convention. */ declare function _getConnectionCountForTesting(): number; /** * Close every pooled connection and clear ref-counts. Test-only helper. * * @internal */ declare function _internal_resetConnections(): Promise; /** @deprecated Renamed to {@link _internal_resetConnections} per the org `_internal_` convention. */ declare function _resetConnectionsForTesting(): Promise; //#endregion //#region src/logger.d.ts /** * Context object for logger methods. * * This type includes reserved keys that provide consistent naming * for common logging context properties. * * @property error - Error object or error details */ type LoggerContext = Record & { error?: unknown; }; /** * Logger interface for amqp-contract packages. * * Provides a simple logging abstraction that can be implemented by users * to integrate with their preferred logging framework. * * @example * ```typescript * // Simple console logger implementation * const logger: Logger = { * debug: (message, context) => console.debug(message, context), * info: (message, context) => console.info(message, context), * warn: (message, context) => console.warn(message, context), * error: (message, context) => console.error(message, context), * }; * ``` */ type Logger = { /** * Log debug level messages * @param message - The log message * @param context - Optional context to include with the log */ debug(message: string, context?: LoggerContext): void; /** * Log info level messages * @param message - The log message * @param context - Optional context to include with the log */ info(message: string, context?: LoggerContext): void; /** * Log warning level messages * @param message - The log message * @param context - Optional context to include with the log */ warn(message: string, context?: LoggerContext): void; /** * Log error level messages * @param message - The log message * @param context - Optional context to include with the log */ error(message: string, context?: LoggerContext): void; }; //#endregion //#region src/parsing.d.ts /** * Parse a `Buffer` as JSON, mapping any `JSON.parse` exception to the * caller-supplied error type. * * Use this in consume / reply paths where a parse failure is a typed value, * not a thrown exception — the caller decides how to translate the raw error * into a domain-level error (e.g. {@link TechnicalError}). * * @typeParam E - The error type produced by `errorFn`. * @param buffer - The raw message body to parse. * @param errorFn - Callback invoked with the underlying `JSON.parse` error. * @returns A `Result` containing the parsed `unknown` value or the mapped error. * * @example * ```typescript * const parsed = safeJsonParse( * msg.content, * (error) => new TechnicalError("Failed to parse JSON", error), * ); * ``` */ declare function safeJsonParse(buffer: Buffer, errorFn: (raw: unknown) => E): Result; //#endregion //#region src/setup.d.ts /** * Setup AMQP topology (exchanges, queues, and bindings) from a contract definition. * * This function sets up the complete AMQP topology in the correct order: * 1. Assert all exchanges defined in the contract * 2. Validate dead letter exchanges are declared before referencing them * 3. Assert all queues with their configurations (including dead letter settings) * 4. Create all bindings (queue-to-exchange and exchange-to-exchange) * * @param channel - The AMQP channel to use for topology setup * @param contract - The contract definition containing the topology specification * @throws {AggregateError} If any exchanges, queues, or bindings fail to be created * @throws {TechnicalError} If a queue references a dead letter exchange not declared in the contract * * @example * ```typescript * const channel = await connection.createChannel(); * await setupAmqpTopology(channel, contract); * ``` */ declare function setupAmqpTopology(channel: Channel, contract: ContractDefinition): Promise; //#endregion //#region src/telemetry.d.ts /** * Semantic conventions for AMQP messaging following OpenTelemetry standards. * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/ */ declare const MessagingSemanticConventions: { readonly MESSAGING_SYSTEM: "messaging.system"; readonly MESSAGING_DESTINATION: "messaging.destination.name"; readonly MESSAGING_DESTINATION_KIND: "messaging.destination.kind"; readonly MESSAGING_OPERATION: "messaging.operation"; readonly MESSAGING_RABBITMQ_ROUTING_KEY: "messaging.rabbitmq.destination.routing_key"; readonly MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: "messaging.rabbitmq.message.delivery_tag"; readonly AMQP_PUBLISHER_NAME: "amqp.publisher.name"; readonly AMQP_CONSUMER_NAME: "amqp.consumer.name"; readonly ERROR_TYPE: "error.type"; readonly MESSAGING_SYSTEM_RABBITMQ: "rabbitmq"; readonly MESSAGING_DESTINATION_KIND_EXCHANGE: "exchange"; readonly MESSAGING_DESTINATION_KIND_QUEUE: "queue"; readonly MESSAGING_OPERATION_PUBLISH: "publish"; readonly MESSAGING_OPERATION_PROCESS: "process"; }; /** * Telemetry provider for AMQP operations. * Uses lazy loading to gracefully handle cases where OpenTelemetry is not installed. */ type TelemetryProvider = { /** * Get a tracer instance for creating spans. * Returns undefined if OpenTelemetry is not available. */ getTracer: () => Tracer | undefined; /** * Get a counter for messages published. * Returns undefined if OpenTelemetry is not available. */ getPublishCounter: () => Counter | undefined; /** * Get a counter for messages consumed. * Returns undefined if OpenTelemetry is not available. */ getConsumeCounter: () => Counter | undefined; /** * Get a histogram for publish latency. * Returns undefined if OpenTelemetry is not available. */ getPublishLatencyHistogram: () => Histogram | undefined; /** * Get a histogram for consume/process latency. * Returns undefined if OpenTelemetry is not available. */ getConsumeLatencyHistogram: () => Histogram | undefined; /** * Get a counter for RPC replies that arrive after the caller has gone away * (timeout, cancellation, or unknown correlationId). Returns undefined if * OpenTelemetry is not available. */ getLateRpcReplyCounter: () => Counter | undefined; }; /** * Default telemetry provider that uses OpenTelemetry API if available. */ declare const defaultTelemetryProvider: TelemetryProvider; /** * Create a span for a publish operation. * Returns undefined if OpenTelemetry is not available. */ declare function startPublishSpan(provider: TelemetryProvider, exchangeName: string, routingKey: string | undefined, attributes?: Attributes): Span | undefined; /** * Create a span for a consume/process operation. * Returns undefined if OpenTelemetry is not available. */ declare function startConsumeSpan(provider: TelemetryProvider, queueName: string, consumerName: string, attributes?: Attributes): Span | undefined; /** * End a span with success status. */ declare function endSpanSuccess(span: Span | undefined): void; /** * End a span with error status. */ declare function endSpanError(span: Span | undefined, error: Error): void; /** * Record a publish metric. */ declare function recordPublishMetric(provider: TelemetryProvider, exchangeName: string, routingKey: string | undefined, success: boolean, durationMs: number): void; /** * Record a consume metric. */ declare function recordConsumeMetric(provider: TelemetryProvider, queueName: string, consumerName: string, success: boolean, durationMs: number): void; /** * Record an RPC reply that arrived after the caller stopped waiting. * * @param reason - Why the reply was orphaned. `"unknown-correlation-id"` is * the typical "caller already timed out" case; `"missing-correlation-id"` * means the broker delivered a reply with no correlationId at all (a * protocol violation by the responder). */ declare function recordLateRpcReply(provider: TelemetryProvider, reason: "unknown-correlation-id" | "missing-correlation-id"): void; /** * Reset the cached OpenTelemetry API module and instruments. * For testing purposes only. * @internal */ declare function _internal_resetTelemetryCache(): void; /** @deprecated Renamed to {@link _internal_resetTelemetryCache} per the org `_internal_` convention. */ declare function _resetTelemetryCacheForTesting(): void; //#endregion export { AmqpClient, type AmqpClientOptions, type ConsumeCallback, type ConsumerOptions, DEFAULT_CONNECT_TIMEOUT_MS, type Logger, type LoggerContext, MessageValidationError, MessagingSemanticConventions, type PublishOptions, RPC_ERROR_CODE_HEADER, RpcError, TechnicalError, type TelemetryProvider, _getConnectionCountForTesting, _internal_getConnectionCount, _internal_resetConnections, _internal_resetTelemetryCache, _resetConnectionsForTesting, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, isRpcError, recordConsumeMetric, recordLateRpcReply, recordPublishMetric, rpcError, safeJsonParse, setupAmqpTopology, startConsumeSpan, startPublishSpan }; //# sourceMappingURL=index.d.mts.map