{"version":3,"file":"index.mjs","names":[],"sources":["../src/connection-manager.ts","../src/errors.ts","../src/setup.ts","../src/amqp-client.ts","../src/parsing.ts","../src/telemetry.ts"],"sourcesContent":["import amqp, {\n  AmqpConnectionManager,\n  AmqpConnectionManagerOptions,\n  ConnectionUrl,\n} from \"amqp-connection-manager\";\n\n/**\n * Connection manager singleton for sharing AMQP connections across clients.\n *\n * This singleton implements connection pooling to avoid creating multiple connections\n * to the same broker, which is a RabbitMQ best practice. Connections are identified\n * by their URLs and connection options, and reference counting ensures connections\n * are only closed when all clients have released them.\n *\n * @example\n * ```typescript\n * const manager = ConnectionManagerSingleton.getInstance();\n * const connection = manager.getConnection(['amqp://localhost']);\n * // ... use connection ...\n * await manager.releaseConnection(['amqp://localhost']);\n * ```\n */\nexport class ConnectionManagerSingleton {\n  private static instance: ConnectionManagerSingleton;\n  private connections: Map<string, AmqpConnectionManager> = new Map();\n  private refCounts: Map<string, number> = new Map();\n\n  private constructor() {}\n\n  /**\n   * Get the singleton instance of the connection manager.\n   *\n   * @returns The singleton instance\n   */\n  static getInstance(): ConnectionManagerSingleton {\n    if (!ConnectionManagerSingleton.instance) {\n      ConnectionManagerSingleton.instance = new ConnectionManagerSingleton();\n    }\n    return ConnectionManagerSingleton.instance;\n  }\n\n  /**\n   * Get or create a connection for the given URLs and options.\n   *\n   * If a connection already exists with the same URLs and options, it is reused\n   * and its reference count is incremented. Otherwise, a new connection is created.\n   *\n   * @param urls - AMQP broker URL(s)\n   * @param connectionOptions - Optional connection configuration\n   * @returns The AMQP connection manager instance\n   */\n  getConnection(\n    urls: ConnectionUrl[],\n    connectionOptions?: AmqpConnectionManagerOptions,\n  ): AmqpConnectionManager {\n    // Create a key based on URLs and connection options\n    const key = this.createConnectionKey(urls, connectionOptions);\n\n    if (!this.connections.has(key)) {\n      const connection = amqp.connect(urls, connectionOptions);\n      this.connections.set(key, connection);\n      this.refCounts.set(key, 0);\n    }\n\n    // Increment reference count\n    this.refCounts.set(key, (this.refCounts.get(key) ?? 0) + 1);\n\n    return this.connections.get(key)!;\n  }\n\n  /**\n   * Release a connection reference.\n   *\n   * Decrements the reference count for the connection. If the count reaches zero,\n   * the connection is closed and removed from the pool.\n   *\n   * @param urls - AMQP broker URL(s) used to identify the connection\n   * @param connectionOptions - Optional connection configuration used to identify the connection\n   * @returns A promise that resolves when the connection is released (and closed if necessary)\n   */\n  async releaseConnection(\n    urls: ConnectionUrl[],\n    connectionOptions?: AmqpConnectionManagerOptions,\n  ): Promise<void> {\n    const key = this.createConnectionKey(urls, connectionOptions);\n    const refCount = this.refCounts.get(key) ?? 0;\n\n    if (refCount <= 1) {\n      // Last reference - close and remove connection\n      const connection = this.connections.get(key);\n      if (connection) {\n        await connection.close();\n        this.connections.delete(key);\n        this.refCounts.delete(key);\n      }\n    } else {\n      // Decrement reference count\n      this.refCounts.set(key, refCount - 1);\n    }\n  }\n\n  /**\n   * Create a unique key for a connection based on URLs and options.\n   *\n   * The key is deterministic: same URLs and options always produce the same key,\n   * enabling connection reuse.\n   *\n   * @param urls - AMQP broker URL(s)\n   * @param connectionOptions - Optional connection configuration\n   * @returns A unique string key identifying the connection\n   */\n  private createConnectionKey(\n    urls: ConnectionUrl[],\n    connectionOptions?: AmqpConnectionManagerOptions,\n  ): string {\n    // Create a deterministic key from URLs and options.\n    // Use JSON.stringify for URLs to avoid ambiguity (e.g., ['a,b'] vs ['a', 'b']).\n    //\n    // INTENTIONAL: URL order is part of the connection key — `['a','b']` and\n    // `['b','a']` get *different* pooled connections. amqp-connection-manager\n    // treats the URL list as a failover list where the first entry is the\n    // preferred broker, so two callers passing the same set of URLs in\n    // different orders are asking for semantically different connections (one\n    // prefers `a`, the other prefers `b`). Do not \"fix\" this by sorting — it\n    // would silently merge those two connections and pin one caller's failover\n    // preference onto the other.\n    const urlsStr = JSON.stringify(urls);\n    // Sort object keys for deterministic serialization of connection options\n    const optsStr = connectionOptions ? this.serializeOptions(connectionOptions) : \"\";\n    return `${urlsStr}::${optsStr}`;\n  }\n\n  /**\n   * Serialize connection options to a deterministic string.\n   *\n   * @param options - Connection options to serialize\n   * @returns A JSON string with sorted keys for deterministic comparison\n   */\n  private serializeOptions(options: AmqpConnectionManagerOptions): string {\n    // Create a deterministic string representation by deeply sorting all object keys\n    const sorted = this.deepSort(options);\n    return JSON.stringify(sorted);\n  }\n\n  /**\n   * Deep sort an object's keys for deterministic serialization.\n   *\n   * @param value - The value to deep sort (can be object, array, or primitive)\n   * @returns The value with all object keys sorted alphabetically\n   */\n  private deepSort(value: unknown): unknown {\n    if (Array.isArray(value)) {\n      return value.map((item) => this.deepSort(item));\n    }\n\n    if (value !== null && typeof value === \"object\") {\n      const obj = value as Record<string, unknown>;\n      const sortedKeys = Object.keys(obj).sort();\n      const result: Record<string, unknown> = {};\n\n      for (const key of sortedKeys) {\n        result[key] = this.deepSort(obj[key]);\n      }\n\n      return result;\n    }\n\n    return value;\n  }\n\n  /**\n   * Get the number of active pooled connections.\n   *\n   * @internal\n   */\n  _getConnectionCountForTesting(): number {\n    return this.connections.size;\n  }\n\n  /**\n   * Reset all cached connections (for testing purposes)\n   * @internal\n   */\n  async _resetForTesting(): Promise<void> {\n    // Close all connections before clearing\n    const closePromises = Array.from(this.connections.values()).map((conn) => conn.close());\n    await Promise.all(closePromises);\n    this.connections.clear();\n    this.refCounts.clear();\n  }\n}\n\n/**\n * Number of active pooled connections. Test-only helper — exposed in lieu of\n * the underlying singleton, which is intentionally not part of the public API\n * (mutating it from outside the library can break in-flight clients sharing a\n * connection).\n *\n * @internal\n */\nexport function _internal_getConnectionCount(): number {\n  return ConnectionManagerSingleton.getInstance()._getConnectionCountForTesting();\n}\n\n/** @deprecated Renamed to {@link _internal_getConnectionCount} per the org `_internal_` convention. */\nexport function _getConnectionCountForTesting(): number {\n  return _internal_getConnectionCount();\n}\n\n/**\n * Close every pooled connection and clear ref-counts. Test-only helper.\n *\n * @internal\n */\nexport function _internal_resetConnections(): Promise<void> {\n  return ConnectionManagerSingleton.getInstance()._resetForTesting();\n}\n\n/** @deprecated Renamed to {@link _internal_resetConnections} per the org `_internal_` convention. */\nexport function _resetConnectionsForTesting(): Promise<void> {\n  return _internal_resetConnections();\n}\n","import { summarizeIssues } from \"@amqp-contract/contract\";\nimport { TaggedError } from \"unthrown\";\n\n/**\n * Error for technical/runtime failures that cannot be prevented by TypeScript.\n *\n * This includes AMQP connection failures, channel issues, validation failures,\n * and other runtime errors. This error is shared across core, worker, and client packages.\n *\n * Built on unthrown's {@link TaggedError}, so it carries a `_tag` of\n * `\"@amqp-contract/TechnicalError\"` for exhaustive dispatch via `matchTags`. The\n * tag is namespaced to avoid colliding with other libraries' tags in a shared\n * `matchTags`; the human-facing `Error.name` is kept bare (`\"TechnicalError\"`).\n * Remains a real `Error` (and a *modeled* error — it lives in the `E` channel of\n * a `Result`, never the `Defect` channel).\n */\nexport class TechnicalError extends TaggedError(\"@amqp-contract/TechnicalError\", {\n  name: \"TechnicalError\",\n})<{\n  cause?: unknown;\n}> {\n  constructor(message: string, cause?: unknown) {\n    super({ cause });\n    this.message = message;\n  }\n}\n\n/**\n * Error thrown when message validation fails (payload or headers).\n *\n * Used by both the client (publish-time payload validation) and the worker\n * (consume-time payload and headers validation). Carries a `_tag` of\n * `\"@amqp-contract/MessageValidationError\"` (namespaced to avoid collisions);\n * the `Error.name` is kept bare (`\"MessageValidationError\"`).\n *\n * @param source - The name of the publisher or consumer that triggered the validation\n * @param issues - The validation issues from the Standard Schema validation\n */\nexport class MessageValidationError extends TaggedError(\"@amqp-contract/MessageValidationError\", {\n  name: \"MessageValidationError\",\n})<{\n  source: string;\n  issues: unknown;\n}> {\n  constructor(source: string, issues: unknown) {\n    super({ source, issues });\n    // Render the issues into the message via the shared formatter when they\n    // look like Standard Schema issues; keep the plain message otherwise\n    // (issues is typed unknown — defensive against foreign shapes).\n    const summary = Array.isArray(issues)\n      ? ((): string | undefined => {\n          try {\n            return summarizeIssues(issues as Parameters<typeof summarizeIssues>[0]);\n          } catch {\n            return undefined;\n          }\n        })()\n      : undefined;\n    this.message = summary\n      ? `Message validation failed for \"${source}\": ${summary}`\n      : `Message validation failed for \"${source}\"`;\n  }\n}\n\n/**\n * AMQP message header carrying the error code of a typed RPC error reply.\n *\n * A reply message with this header is an error reply: its body is\n * `{ message, data }` where `data` conforms to the error's declared schema in\n * the RPC's `errors` map. A reply without it is a regular success reply whose\n * body is the response payload — so success replies are wire-compatible with\n * contracts that declare no errors.\n */\nexport const RPC_ERROR_CODE_HEADER = \"x-amqp-contract-error-code\";\n\n/**\n * A typed, contract-declared RPC error — the business-failure channel of an\n * RPC, as opposed to the transport failures modeled by {@link TechnicalError}.\n *\n * Declared per-RPC via `defineRpc(queue, { request, response, errors })`,\n * where each error code maps to a message definition validating the error's\n * `data` payload. A worker handler surfaces one by returning\n * `Err(rpcError(code, data))`; the worker validates `data` against the\n * declared schema, publishes an error reply, and acks the request (business\n * errors are not retried). The caller's `client.call(...)` resolves to\n * `Err(RpcError<code, data>)` with `data` re-validated on arrival.\n *\n * Carries a `_tag` of `\"@amqp-contract/RpcError\"` for exhaustive dispatch via\n * `matchTags`; the `Error.name` is kept bare (`\"RpcError\"`). Discriminate\n * between codes on the `code` property.\n */\nexport class RpcError<TCode extends string = string, TData = unknown> extends TaggedError(\n  \"@amqp-contract/RpcError\",\n  { name: \"RpcError\" },\n)<{\n  code: string;\n  data: unknown;\n}> {\n  declare readonly code: TCode;\n  declare readonly data: TData;\n\n  constructor(code: TCode, data: TData, message?: string) {\n    super({ code, data });\n    this.message = message ?? `RPC failed with error \"${code}\"`;\n  }\n}\n\n/**\n * Type guard to check if an error is an {@link RpcError}.\n *\n * Narrowing to a specific code (and thus a typed `data`) is done on the\n * `code` property after the guard, or via `matchTags` on the `_tag`.\n */\nexport function isRpcError(error: unknown): error is RpcError {\n  return error instanceof RpcError;\n}\n\n/**\n * Create an {@link RpcError} with less verbosity.\n *\n * The code/data pair must match one of the entries declared in the RPC's\n * `errors` map — the handler's return type enforces this at compile time, and\n * the worker validates `data` against the declared schema at runtime before\n * replying.\n *\n * @param code - The error code, as declared in the RPC's `errors` map\n * @param data - The error data, validated against the declared schema\n * @param message - Optional human-readable message (defaults to a generic one)\n *\n * @example\n * ```typescript\n * import { rpcError } from '@amqp-contract/worker';\n * import { ErrAsync } from 'unthrown';\n *\n * const handler = ({ payload }) => {\n *   if (!orders.has(payload.orderId)) {\n *     return ErrAsync(rpcError('ORDER_NOT_FOUND', { orderId: payload.orderId }));\n *   }\n *   // ...\n * };\n * ```\n */\nexport function rpcError<TCode extends string, TData>(\n  code: TCode,\n  data: TData,\n  message?: string,\n): RpcError<TCode, TData> {\n  return new RpcError(code, data, message);\n}\n","import type { ContractDefinition } from \"@amqp-contract/contract\";\nimport { extractQueue } from \"@amqp-contract/contract\";\nimport type { Channel } from \"amqplib\";\nimport { TechnicalError } from \"./errors.js\";\n\n/**\n * Setup AMQP topology (exchanges, queues, and bindings) from a contract definition.\n *\n * This function sets up the complete AMQP topology in the correct order:\n * 1. Assert all exchanges defined in the contract\n * 2. Validate dead letter exchanges are declared before referencing them\n * 3. Assert all queues with their configurations (including dead letter settings)\n * 4. Create all bindings (queue-to-exchange and exchange-to-exchange)\n *\n * @param channel - The AMQP channel to use for topology setup\n * @param contract - The contract definition containing the topology specification\n * @throws {AggregateError} If any exchanges, queues, or bindings fail to be created\n * @throws {TechnicalError} If a queue references a dead letter exchange not declared in the contract\n *\n * @example\n * ```typescript\n * const channel = await connection.createChannel();\n * await setupAmqpTopology(channel, contract);\n * ```\n */\nexport async function setupAmqpTopology(\n  channel: Channel,\n  contract: ContractDefinition,\n): Promise<void> {\n  // Setup exchanges. The AMQP default exchange (name \"\") is implicit; RabbitMQ\n  // does not allow asserting it, so we skip empty-named exchange entries.\n  const exchanges = Object.values(contract.exchanges ?? {}).filter((e) => e.name !== \"\");\n  const exchangeResults = await Promise.allSettled(\n    exchanges.map((exchange) =>\n      channel.assertExchange(exchange.name, exchange.type, {\n        ...(exchange.durable !== undefined && { durable: exchange.durable }),\n        ...(exchange.autoDelete !== undefined && { autoDelete: exchange.autoDelete }),\n        ...(exchange.internal !== undefined && { internal: exchange.internal }),\n        ...(exchange.arguments !== undefined && { arguments: exchange.arguments }),\n      }),\n    ),\n  );\n  const exchangeErrors = exchangeResults\n    .map((result, i) => ({ result, name: exchanges[i]!.name }))\n    .filter(\n      (entry): entry is { result: PromiseRejectedResult; name: string } =>\n        entry.result.status === \"rejected\",\n    );\n  if (exchangeErrors.length > 0) {\n    const names = exchangeErrors.map((e) => e.name).join(\", \");\n    throw new AggregateError(\n      exchangeErrors.map(({ result }) => result.reason),\n      `Failed to setup exchanges: ${names}`,\n    );\n  }\n\n  // Validate dead letter exchanges before setting up queues\n  for (const queueEntry of Object.values(contract.queues ?? {})) {\n    const queue = extractQueue(queueEntry);\n    if (queue.deadLetter) {\n      const dlxName = queue.deadLetter.exchange.name;\n      const exchangeExists = Object.values(contract.exchanges ?? {}).some(\n        (exchange) => exchange.name === dlxName,\n      );\n\n      if (!exchangeExists) {\n        throw new TechnicalError(\n          `Queue \"${queue.name}\" references dead letter exchange \"${dlxName}\" which is not declared in the contract. ` +\n            `Add the exchange to contract.exchanges to ensure it is created before the queue.`,\n        );\n      }\n    }\n  }\n\n  // Setup queues\n  const queueEntries = Object.values(contract.queues ?? {});\n  const queueResults = await Promise.allSettled(\n    queueEntries.map((queueEntry) => {\n      const queue = extractQueue(queueEntry);\n      // Build queue arguments, merging dead letter configuration and queue type\n      const queueArguments: Record<string, unknown> = { ...queue.arguments };\n\n      // Set queue type\n      queueArguments[\"x-queue-type\"] = queue.type;\n\n      if (queue.deadLetter) {\n        queueArguments[\"x-dead-letter-exchange\"] = queue.deadLetter.exchange.name;\n        if (queue.deadLetter.routingKey) {\n          queueArguments[\"x-dead-letter-routing-key\"] = queue.deadLetter.routingKey;\n        }\n      }\n\n      // Handle type-specific properties using discriminated union\n      if (queue.type === \"quorum\") {\n        return channel.assertQueue(queue.name, {\n          durable: true, // Quorum queues are always durable\n          arguments: queueArguments,\n        });\n      }\n\n      if (queue.maxPriority !== undefined) {\n        queueArguments[\"x-max-priority\"] = queue.maxPriority;\n      }\n\n      // Classic queue\n      return channel.assertQueue(queue.name, {\n        ...(queue.durable !== undefined && { durable: queue.durable }),\n        ...(queue.exclusive !== undefined && { exclusive: queue.exclusive }),\n        ...(queue.autoDelete !== undefined && { autoDelete: queue.autoDelete }),\n        arguments: queueArguments,\n      });\n    }),\n  );\n  const queueErrors = queueResults\n    .map((result, i) => ({ result, name: extractQueue(queueEntries[i]!).name }))\n    .filter(\n      (entry): entry is { result: PromiseRejectedResult; name: string } =>\n        entry.result.status === \"rejected\",\n    );\n  if (queueErrors.length > 0) {\n    const names = queueErrors.map((e) => e.name).join(\", \");\n    throw new AggregateError(\n      queueErrors.map(({ result }) => result.reason),\n      `Failed to setup queues: ${names}`,\n    );\n  }\n\n  // Setup bindings\n  const bindings = Object.values(contract.bindings ?? {});\n  const bindingResults = await Promise.allSettled(\n    bindings.map((binding) => {\n      if (binding.type === \"queue\") {\n        return channel.bindQueue(\n          binding.queue.name,\n          binding.exchange.name,\n          binding.routingKey ?? \"\",\n          binding.arguments,\n        );\n      }\n\n      return channel.bindExchange(\n        binding.destination.name,\n        binding.source.name,\n        binding.routingKey ?? \"\",\n        binding.arguments,\n      );\n    }),\n  );\n  const bindingErrors = bindingResults\n    .map((result, i) => {\n      const binding = bindings[i]!;\n      const name =\n        binding.type === \"queue\"\n          ? `${binding.exchange.name} -> ${binding.queue.name}`\n          : `${binding.source.name} -> ${binding.destination.name}`;\n      return { result, name };\n    })\n    .filter(\n      (entry): entry is { result: PromiseRejectedResult; name: string } =>\n        entry.result.status === \"rejected\",\n    );\n  if (bindingErrors.length > 0) {\n    const names = bindingErrors.map((e) => e.name).join(\", \");\n    throw new AggregateError(\n      bindingErrors.map(({ result }) => result.reason),\n      `Failed to setup bindings: ${names}`,\n    );\n  }\n}\n","import type { ContractDefinition } from \"@amqp-contract/contract\";\nimport type {\n  AmqpConnectionManager,\n  AmqpConnectionManagerOptions,\n  ChannelWrapper,\n  ConnectionUrl,\n  CreateChannelOpts,\n} from \"amqp-connection-manager\";\nimport type { Channel, ConsumeMessage, Options } from \"amqplib\";\nimport {\n  Err,\n  ErrAsync,\n  fromPromise,\n  fromSafePromise,\n  Ok,\n  type AsyncResult,\n  type Result,\n} from \"unthrown\";\nimport { ConnectionManagerSingleton } from \"./connection-manager.js\";\nimport { TechnicalError } from \"./errors.js\";\nimport { setupAmqpTopology } from \"./setup.js\";\n\n/**\n * Invoke a SetupFunc, handling both callback-based and promise-based signatures.\n * Uses Function.length to distinguish (same approach as promise-breaker).\n * @internal\n */\nfunction callSetupFunc(\n  setup: NonNullable<CreateChannelOpts[\"setup\"]>,\n  channel: Channel,\n): Promise<void> {\n  if (setup.length >= 2) {\n    return new Promise<void>((resolve, reject) => {\n      (setup as (channel: Channel, callback: (error?: Error) => void) => void)(\n        channel,\n        (error?: Error) => {\n          if (error) reject(error);\n          else resolve();\n        },\n      );\n    });\n  }\n  return (setup as (channel: Channel) => Promise<void>)(channel);\n}\n\n/**\n * Default time `waitForConnect` will wait for the broker before erroring out.\n * Defaulting to a finite value (rather than waiting forever) means a fail-fast\n * developer experience: a misconfigured URL, a down broker, or wrong\n * credentials surface as an `err` within 30 seconds. Pass `null`\n * explicitly to disable the timeout — `Infinity` and other non-finite values\n * are also coerced to \"no timeout\" because Node's `setTimeout` clamps large\n * delays to ~24.8 days and silently fires near-immediately on `Infinity`.\n */\nexport const DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\n\n/**\n * Normalise the user-supplied connect timeout to either a positive finite\n * number of milliseconds, or `null` (no timeout). `Infinity`, `NaN`, and\n * non-positive values all map to `null` rather than being passed to\n * `setTimeout` — see {@link DEFAULT_CONNECT_TIMEOUT_MS}.\n */\nfunction resolveConnectTimeoutMs(input: number | null | undefined): number | null {\n  if (input === null) return null;\n  if (input === undefined) return DEFAULT_CONNECT_TIMEOUT_MS;\n  if (!Number.isFinite(input) || input <= 0) return null;\n  return input;\n}\n\n/**\n * Options for creating an AMQP client.\n *\n * @property urls - AMQP broker URL(s). Multiple URLs provide failover support.\n * @property connectionOptions - Optional connection configuration (heartbeat, reconnect settings, etc.).\n * @property channelOptions - Optional channel configuration options.\n * @property connectTimeoutMs - Maximum time in ms to wait for the channel to\n *   become ready in `waitForConnect`. Defaults to {@link DEFAULT_CONNECT_TIMEOUT_MS}.\n *   Pass `null` to disable the timeout entirely (amqp-connection-manager will\n *   retry indefinitely).\n */\nexport type AmqpClientOptions = {\n  urls: ConnectionUrl[];\n  connectionOptions?: AmqpConnectionManagerOptions | undefined;\n  channelOptions?: Partial<CreateChannelOpts> | undefined;\n  connectTimeoutMs?: number | null | undefined;\n};\n\n/**\n * Callback type for consuming messages.\n */\nexport type ConsumeCallback = (msg: ConsumeMessage | null) => void | Promise<void>;\n\n/**\n * Publish options for `AmqpClient.publish` / `AmqpClient.sendToQueue`.\n *\n * Currently a re-export of amqplib's `Options.Publish`. A previous version of\n * this type also exposed a `timeout` field, but that field never had a\n * meaningful AMQP-level effect in this codebase and has been removed to avoid\n * suggesting behaviour we do not provide. (`amqp-connection-manager`'s own\n * `publishTimeout` channel option is unrelated and is configured at channel\n * creation, not per-publish.)\n */\nexport type PublishOptions = Options.Publish;\n\n/**\n * Consume options that extend amqplib's `Options.Consume` with an optional\n * per-consumer prefetch count.\n *\n * `prefetch` is intercepted by {@link AmqpClient.consume}: it is stripped from\n * the options handed to the underlying `channelWrapper.consume(...)` call\n * (since amqplib's `Options.Consume` does not include it) and applied via\n * `channel.prefetch(count, false)` registered through `addSetup` *before* the\n * consume so the value is in effect when the consumer starts and is reapplied\n * automatically on channel reconnect.\n */\nexport type ConsumerOptions = Options.Consume & {\n  /** Per-consumer prefetch count. Applied before `channel.consume(...)`. */\n  prefetch?: number;\n};\n\n/**\n * AMQP client that manages connections and channels with automatic topology setup.\n *\n * This class handles:\n * - Connection management with automatic reconnection via amqp-connection-manager\n * - Connection pooling and sharing across instances with the same URLs\n * - Automatic AMQP topology setup (exchanges, queues, bindings) from contract\n * - Channel creation with JSON serialization enabled by default\n *\n * All operations return `AsyncResult<T, TechnicalError>` for consistent error handling.\n *\n * @example\n * ```typescript\n * const client = new AmqpClient(contract, {\n *   urls: ['amqp://localhost'],\n *   connectionOptions: { heartbeatIntervalInSeconds: 30 }\n * });\n *\n * // Wait for connection (AsyncResult is thenable)\n * await client.waitForConnect();\n *\n * // Publish a message\n * const result = await client.publish('exchange', 'routingKey', { data: 'value' });\n *\n * // Close when done\n * await client.close();\n * ```\n */\nexport class AmqpClient {\n  private readonly connection: AmqpConnectionManager;\n  private readonly channelWrapper: ChannelWrapper;\n  private readonly urls: ConnectionUrl[];\n  private readonly connectionOptions?: AmqpConnectionManagerOptions;\n  /** Resolved timeout in ms; `null` means \"wait forever\". */\n  private readonly connectTimeoutMs: number | null;\n  /**\n   * Per-consumer prefetch setup functions registered via `addSetup` so they\n   * can be removed in {@link cancel} once the consumer is gone — otherwise\n   * the channel wrapper would replay the cancelled consumer's QoS on every\n   * reconnect and silently apply it to subsequent consumers.\n   *\n   * @internal\n   */\n  private readonly prefetchSetups: Map<string, (channel: Channel) => Promise<void>> = new Map();\n\n  /**\n   * Create a new AMQP client instance.\n   *\n   * The client will automatically:\n   * - Get or create a shared connection using the singleton pattern\n   * - Set up AMQP topology (exchanges, queues, bindings) from the contract\n   * - Create a channel with JSON serialization enabled\n   *\n   * @param contract - The contract definition specifying the AMQP topology\n   * @param options - Client configuration options\n   */\n  constructor(\n    private readonly contract: ContractDefinition,\n    options: AmqpClientOptions,\n  ) {\n    // Store for cleanup\n    this.urls = options.urls;\n    if (options.connectionOptions !== undefined) {\n      this.connectionOptions = options.connectionOptions;\n    }\n    // Resolve connect timeout: explicit null disables it; undefined (the common\n    // case) gets the fail-fast default. Finite positive numbers pass through;\n    // any other numeric value (Infinity, NaN, ≤ 0) is coerced to null because\n    // Node's `setTimeout` clamps large delays to ~24.8 days and silently fires\n    // near-immediately on Infinity — neither is what a caller asking for \"no\n    // timeout\" expects.\n    this.connectTimeoutMs = resolveConnectTimeoutMs(options.connectTimeoutMs);\n\n    // Always use singleton to get/create connection\n    const singleton = ConnectionManagerSingleton.getInstance();\n    this.connection = singleton.getConnection(options.urls, options.connectionOptions);\n\n    // Create default setup function that calls setupAmqpTopology\n    const defaultSetup = (channel: Channel) => setupAmqpTopology(channel, this.contract);\n\n    // Destructure setup from channelOptions to handle it separately\n    const { setup: userSetup, ...otherChannelOptions } = options.channelOptions ?? {};\n\n    // Merge user-provided channel options with defaults\n    const channelOpts: CreateChannelOpts = {\n      confirm: true,\n      json: true,\n      setup: defaultSetup,\n      ...otherChannelOptions,\n    };\n\n    // If user provided a custom setup, wrap it to call both\n    if (userSetup) {\n      channelOpts.setup = async (channel: Channel) => {\n        await defaultSetup(channel);\n        await callSetupFunc(userSetup, channel);\n      };\n    }\n\n    this.channelWrapper = this.connection.createChannel(channelOpts);\n  }\n\n  /**\n   * Get the underlying connection manager\n   *\n   * This method exposes the AmqpConnectionManager instance that this client uses.\n   * The connection is automatically shared across all AmqpClient instances that\n   * use the same URLs and connection options.\n   *\n   * @returns The AmqpConnectionManager instance used by this client\n   */\n  getConnection(): AmqpConnectionManager {\n    return this.connection;\n  }\n\n  /**\n   * Wait for the channel to be connected and ready.\n   *\n   * If `connectTimeoutMs` was provided in the constructor options, the returned\n   * AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses.\n   * Without a timeout, this waits forever — amqp-connection-manager retries\n   * connections indefinitely and never errors on its own.\n   *\n   * NOTE: When using `AmqpClient` directly (not via `TypedAmqpClient` /\n   * `TypedAmqpWorker`), the constructor has already incremented the pooled\n   * connection's reference count. Callers must invoke `close()` on the error\n   * path to release the connection — `waitForConnect` does not do this\n   * automatically. The typed factories handle this cleanup for you.\n   */\n  waitForConnect(): AsyncResult<void, TechnicalError> {\n    const connectPromise = this.channelWrapper.waitForConnect();\n    const timeoutMs = this.connectTimeoutMs;\n\n    const racedPromise =\n      timeoutMs === null\n        ? connectPromise\n        : new Promise<void>((resolve, reject) => {\n            const handle = setTimeout(() => {\n              reject(new Error(`Timed out waiting for AMQP connection after ${timeoutMs}ms`));\n            }, timeoutMs);\n            connectPromise.then(\n              () => {\n                clearTimeout(handle);\n                resolve();\n              },\n              (error: unknown) => {\n                clearTimeout(handle);\n                reject(error);\n              },\n            );\n          });\n\n    return fromPromise(\n      racedPromise,\n      (error: unknown) =>\n        new TechnicalError(\n          \"Failed to connect to AMQP broker — verify the broker is running and reachable at the configured `urls`\",\n          error,\n        ),\n    );\n  }\n\n  /**\n   * Publish a message to an exchange.\n   *\n   * @returns AsyncResult resolving to `true` if the message was sent, `false` if the channel buffer is full.\n   */\n  publish(\n    exchange: string,\n    routingKey: string,\n    content: Buffer | unknown,\n    options?: PublishOptions,\n  ): AsyncResult<boolean, TechnicalError> {\n    return fromPromise(\n      this.channelWrapper.publish(exchange, routingKey, content, options),\n      (error: unknown) =>\n        new TechnicalError(\n          `Failed to publish message to exchange \"${exchange}\" (routing key \"${routingKey}\")`,\n          error,\n        ),\n    );\n  }\n\n  /**\n   * Publish a message directly to a queue.\n   *\n   * @returns AsyncResult resolving to `true` if the message was sent, `false` if the channel buffer is full.\n   */\n  sendToQueue(\n    queue: string,\n    content: Buffer | unknown,\n    options?: PublishOptions,\n  ): AsyncResult<boolean, TechnicalError> {\n    return fromPromise(\n      this.channelWrapper.sendToQueue(queue, content, options),\n      (error: unknown) =>\n        new TechnicalError(`Failed to publish message to queue \"${queue}\"`, error),\n    );\n  }\n\n  /**\n   * Start consuming messages from a queue.\n   *\n   * If `options.prefetch` is set, a per-consumer prefetch count is applied via\n   * `channel.prefetch(count, false)` registered as a setup function on the\n   * channel wrapper *before* the underlying `consume` call. Registering it via\n   * `addSetup` ensures the prefetch is reapplied automatically on channel\n   * reconnect; using `global=false` scopes it to subsequent consumers on the\n   * channel (RabbitMQ semantics — opposite of intuition: `false` is per-\n   * consumer, `true` is channel-wide).\n   *\n   * `prefetch` is stripped from the options handed to `channelWrapper.consume`\n   * because it is not a valid `amqplib` `Options.Consume` field — leaving it\n   * in would just travel as a no-op key-value pair on the consume frame.\n   *\n   * @returns AsyncResult resolving to the consumer tag.\n   */\n  consume(\n    queue: string,\n    callback: ConsumeCallback,\n    options?: ConsumerOptions,\n  ): AsyncResult<string, TechnicalError> {\n    // Split prefetch out of the options that go to consume(...).\n    const { prefetch, ...consumeOptions } = options ?? {};\n\n    // Validate the prefetch value before forwarding to RabbitMQ. AMQP\n    // basic.qos prefetch-count is an unsigned 16-bit short (0–65535); 0\n    // means unlimited. NaN, negatives, fractions, and out-of-range numbers\n    // were silently dropped by the previous implementation — now they'd\n    // travel to the broker, which either rejects or interprets unexpectedly.\n    if (prefetch !== undefined) {\n      if (!Number.isInteger(prefetch) || prefetch < 0 || prefetch > 65_535) {\n        return ErrAsync(\n          new TechnicalError(\n            `Invalid prefetch: expected a non-negative integer ≤ 65535, got ${String(prefetch)}`,\n          ),\n        );\n      }\n    }\n\n    // Capture the prefetch setup function so it can be removed when the\n    // consumer is cancelled. Otherwise the channel wrapper would replay\n    // it on every reconnect, applying the cancelled consumer's QoS to\n    // subsequent consumers (RabbitMQ's `basic.qos(global=false)` semantics\n    // affect every later consumer on the channel until another `qos`).\n    const prefetchSetup =\n      typeof prefetch === \"number\"\n        ? async (channel: Channel) => {\n            await channel.prefetch(prefetch, false);\n          }\n        : undefined;\n\n    const consumePromise = (async () => {\n      if (prefetchSetup) {\n        // Register prefetch as a channel setup so it is (re)applied on every\n        // reconnect, then start consuming. addSetup() also runs the function\n        // immediately if a channel is already up, so the prefetch is in\n        // effect by the time consume() starts the new consumer.\n        await this.channelWrapper.addSetup(prefetchSetup);\n      }\n      let reply: { consumerTag: string };\n      try {\n        reply = await this.channelWrapper.consume(queue, callback, consumeOptions);\n      } catch (error) {\n        // Roll back the prefetch setup. If consume failed (e.g. queue is\n        // gone), the setup is registered but tied to no consumer; without\n        // this rollback every reconnect would replay it, silently changing\n        // QoS for unrelated consumers on the channel.\n        if (prefetchSetup) {\n          await this.channelWrapper.removeSetup(prefetchSetup).catch(() => {\n            // Best-effort cleanup; swallow so we propagate the original\n            // consume error instead of masking it.\n          });\n        }\n        throw error;\n      }\n      if (prefetchSetup) {\n        this.prefetchSetups.set(reply.consumerTag, prefetchSetup);\n      }\n      return reply;\n    })();\n\n    return fromPromise(\n      consumePromise,\n      (error: unknown) => new TechnicalError(\"Failed to start consuming messages\", error),\n    ).map((reply: { consumerTag: string }) => reply.consumerTag);\n  }\n\n  /**\n   * Cancel a consumer by its consumer tag.\n   */\n  cancel(consumerTag: string): AsyncResult<void, TechnicalError> {\n    return fromPromise(\n      (async () => {\n        // Drop the prefetch setup whether or not the cancel itself succeeds.\n        // If `cancel` rejects (consumer already gone, tag unknown), keeping\n        // the setup registered means every reconnect replays a stale\n        // `basic.qos`, silently changing QoS for unrelated consumers on the\n        // channel. Best-effort cleanup runs in `finally`.\n        const setup = this.prefetchSetups.get(consumerTag);\n        this.prefetchSetups.delete(consumerTag);\n        try {\n          await this.channelWrapper.cancel(consumerTag);\n        } finally {\n          if (setup !== undefined) {\n            await this.channelWrapper.removeSetup(setup).catch(() => {\n              // Best-effort cleanup; swallow so the original cancel error\n              // (if any) propagates unchanged.\n            });\n          }\n        }\n      })(),\n      (error: unknown) => new TechnicalError(\"Failed to cancel consumer\", error),\n    ).map(() => undefined);\n  }\n\n  /**\n   * Acknowledge a message.\n   *\n   * @param msg - The message to acknowledge\n   * @param allUpTo - If true, acknowledge all messages up to and including this one\n   */\n  ack(msg: ConsumeMessage, allUpTo = false): void {\n    this.channelWrapper.ack(msg, allUpTo);\n  }\n\n  /**\n   * Negative acknowledge a message.\n   *\n   * @param msg - The message to nack\n   * @param allUpTo - If true, nack all messages up to and including this one\n   * @param requeue - If true, requeue the message(s)\n   */\n  nack(msg: ConsumeMessage, allUpTo = false, requeue = true): void {\n    this.channelWrapper.nack(msg, allUpTo, requeue);\n  }\n\n  /**\n   * Add a setup function to be called when the channel is created or reconnected.\n   *\n   * This is useful for setting up channel-level configuration like prefetch.\n   *\n   * @param setup - The setup function to add\n   */\n  addSetup(setup: (channel: Channel) => void | Promise<void>): void {\n    this.channelWrapper.addSetup(setup);\n  }\n\n  /**\n   * Register an event listener on the channel wrapper.\n   *\n   * Available events:\n   * - 'connect': Emitted when the channel is (re)connected\n   * - 'close': Emitted when the channel is closed\n   * - 'error': Emitted when an error occurs\n   *\n   * @param event - The event name\n   * @param listener - The event listener\n   */\n  on(event: string, listener: (...args: unknown[]) => void): void {\n    this.channelWrapper.on(event, listener);\n  }\n\n  /**\n   * Close the channel and release the connection reference.\n   *\n   * This will:\n   * - Close the channel wrapper\n   * - Decrease the reference count on the shared connection\n   * - Close the connection if this was the last client using it\n   *\n   * Both steps run regardless of each other's outcome; if both fail, the\n   * errors are wrapped in an AggregateError.\n   */\n  close(): AsyncResult<void, TechnicalError> {\n    const inner = (async (): Promise<Result<void, TechnicalError>> => {\n      const channelResult = await fromPromise(\n        this.channelWrapper.close(),\n        (error: unknown) => new TechnicalError(\"Failed to close channel\", error),\n      );\n      const releaseResult = await fromPromise(\n        ConnectionManagerSingleton.getInstance().releaseConnection(\n          this.urls,\n          this.connectionOptions,\n        ),\n        (error: unknown) => new TechnicalError(\"Failed to release connection\", error),\n      );\n\n      if (channelResult.isErr() && releaseResult.isErr()) {\n        return Err(\n          new TechnicalError(\n            \"Failed to close channel and release connection\",\n            new AggregateError(\n              [channelResult.error, releaseResult.error],\n              \"Failed to close channel and release connection\",\n            ),\n          ),\n        );\n      }\n\n      if (channelResult.isErr()) return channelResult;\n      if (releaseResult.isErr()) return releaseResult;\n      return Ok(undefined);\n    })();\n\n    // `inner` is structured to never reject, so lift it with `fromSafePromise`\n    // and collapse the nested `Result` it resolves to back into the channel.\n    return fromSafePromise(inner).flatMap((result) => result);\n  }\n\n  /**\n   * Reset connection singleton cache (for testing only)\n   * @internal\n   */\n  static async _resetConnectionCacheForTesting(): Promise<void> {\n    await ConnectionManagerSingleton.getInstance()._resetForTesting();\n  }\n}\n","import { fromThrowable, type Result } from \"unthrown\";\n\n/**\n * Parse a `Buffer` as JSON, mapping any `JSON.parse` exception to the\n * caller-supplied error type.\n *\n * Use this in consume / reply paths where a parse failure is a typed value,\n * not a thrown exception — the caller decides how to translate the raw error\n * into a domain-level error (e.g. {@link TechnicalError}).\n *\n * @typeParam E - The error type produced by `errorFn`.\n * @param buffer - The raw message body to parse.\n * @param errorFn - Callback invoked with the underlying `JSON.parse` error.\n * @returns A `Result` containing the parsed `unknown` value or the mapped error.\n *\n * @example\n * ```typescript\n * const parsed = safeJsonParse(\n *   msg.content,\n *   (error) => new TechnicalError(\"Failed to parse JSON\", error),\n * );\n * ```\n */\nexport function safeJsonParse<E>(buffer: Buffer, errorFn: (raw: unknown) => E): Result<unknown, E> {\n  return fromThrowable(() => JSON.parse(buffer.toString()) as unknown, errorFn)();\n}\n","import { createRequire } from \"node:module\";\nimport {\n  type Attributes,\n  type Counter,\n  type Histogram,\n  type Span,\n  type Tracer,\n} from \"@opentelemetry/api\";\n\n/**\n * SpanKind values from OpenTelemetry.\n * Defined as constants to avoid runtime dependency when types are used.\n * @see https://opentelemetry.io/docs/specs/otel/trace/api/#spankind\n */\nconst SpanKind = {\n  /** Producer span represents a message producer */\n  PRODUCER: 3,\n  /** Consumer span represents a message consumer */\n  CONSUMER: 4,\n} as const;\n\n/**\n * Semantic conventions for AMQP messaging following OpenTelemetry standards.\n * @see https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/\n */\nexport const MessagingSemanticConventions = {\n  // Messaging attributes\n  MESSAGING_SYSTEM: \"messaging.system\",\n  MESSAGING_DESTINATION: \"messaging.destination.name\",\n  MESSAGING_DESTINATION_KIND: \"messaging.destination.kind\",\n  MESSAGING_OPERATION: \"messaging.operation\",\n\n  // AMQP/RabbitMQ specific attributes\n  MESSAGING_RABBITMQ_ROUTING_KEY: \"messaging.rabbitmq.destination.routing_key\",\n  MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG: \"messaging.rabbitmq.message.delivery_tag\",\n  AMQP_PUBLISHER_NAME: \"amqp.publisher.name\",\n  AMQP_CONSUMER_NAME: \"amqp.consumer.name\",\n\n  // Error attributes\n  ERROR_TYPE: \"error.type\",\n\n  // Values\n  MESSAGING_SYSTEM_RABBITMQ: \"rabbitmq\",\n  MESSAGING_DESTINATION_KIND_EXCHANGE: \"exchange\",\n  MESSAGING_DESTINATION_KIND_QUEUE: \"queue\",\n  MESSAGING_OPERATION_PUBLISH: \"publish\",\n  MESSAGING_OPERATION_PROCESS: \"process\",\n} as const;\n\n/**\n * Telemetry provider for AMQP operations.\n * Uses lazy loading to gracefully handle cases where OpenTelemetry is not installed.\n */\nexport type TelemetryProvider = {\n  /**\n   * Get a tracer instance for creating spans.\n   * Returns undefined if OpenTelemetry is not available.\n   */\n  getTracer: () => Tracer | undefined;\n\n  /**\n   * Get a counter for messages published.\n   * Returns undefined if OpenTelemetry is not available.\n   */\n  getPublishCounter: () => Counter | undefined;\n\n  /**\n   * Get a counter for messages consumed.\n   * Returns undefined if OpenTelemetry is not available.\n   */\n  getConsumeCounter: () => Counter | undefined;\n\n  /**\n   * Get a histogram for publish latency.\n   * Returns undefined if OpenTelemetry is not available.\n   */\n  getPublishLatencyHistogram: () => Histogram | undefined;\n\n  /**\n   * Get a histogram for consume/process latency.\n   * Returns undefined if OpenTelemetry is not available.\n   */\n  getConsumeLatencyHistogram: () => Histogram | undefined;\n\n  /**\n   * Get a counter for RPC replies that arrive after the caller has gone away\n   * (timeout, cancellation, or unknown correlationId). Returns undefined if\n   * OpenTelemetry is not available.\n   */\n  getLateRpcReplyCounter: () => Counter | undefined;\n};\n\n/**\n * Instrumentation scope name for amqp-contract.\n */\nconst INSTRUMENTATION_SCOPE_NAME = \"@amqp-contract\";\n\n/**\n * Instrumentation scope version, sourced from this package's package.json so\n * the OTel meter version always tracks the released library version. We use\n * `createRequire` rather than a JSON import attribute so the same source builds\n * to ESM, CJS, and runs under bundlers that don't yet understand\n * `import … with { type: \"json\" }`.\n */\nconst INSTRUMENTATION_SCOPE_VERSION: string = (() => {\n  try {\n    const localRequire = createRequire(import.meta.url);\n    const pkg = localRequire(\"../package.json\") as { version?: string };\n    return pkg.version ?? \"0.0.0\";\n  } catch {\n    return \"0.0.0\";\n  }\n})();\n\n// Cache for OpenTelemetry API module and instruments\nlet otelApi: typeof import(\"@opentelemetry/api\") | null | undefined;\nlet cachedTracer: Tracer | undefined;\nlet cachedPublishCounter: Counter | undefined;\nlet cachedConsumeCounter: Counter | undefined;\nlet cachedPublishLatencyHistogram: Histogram | undefined;\nlet cachedConsumeLatencyHistogram: Histogram | undefined;\nlet cachedLateRpcReplyCounter: Counter | undefined;\n\n/**\n * Try to load the OpenTelemetry API module.\n * Returns null if the module is not available.\n */\nfunction tryLoadOpenTelemetryApi(): typeof import(\"@opentelemetry/api\") | null {\n  if (otelApi === undefined) {\n    try {\n      // Dynamic import using require to avoid bundler issues\n      // eslint-disable-next-line @typescript-eslint/no-require-imports\n      otelApi = require(\"@opentelemetry/api\") as typeof import(\"@opentelemetry/api\");\n    } catch {\n      otelApi = null;\n    }\n  }\n  return otelApi;\n}\n\n/**\n * Get or create a tracer instance.\n */\nfunction getTracer(): Tracer | undefined {\n  if (cachedTracer !== undefined) {\n    return cachedTracer;\n  }\n\n  const api = tryLoadOpenTelemetryApi();\n  if (!api) {\n    return undefined;\n  }\n\n  cachedTracer = api.trace.getTracer(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);\n  return cachedTracer;\n}\n\n/**\n * Get or create a meter and its instruments.\n */\nfunction getMeterInstruments(): {\n  publishCounter: Counter | undefined;\n  consumeCounter: Counter | undefined;\n  publishLatencyHistogram: Histogram | undefined;\n  consumeLatencyHistogram: Histogram | undefined;\n  lateRpcReplyCounter: Counter | undefined;\n} {\n  if (cachedPublishCounter !== undefined) {\n    return {\n      publishCounter: cachedPublishCounter,\n      consumeCounter: cachedConsumeCounter,\n      publishLatencyHistogram: cachedPublishLatencyHistogram,\n      consumeLatencyHistogram: cachedConsumeLatencyHistogram,\n      lateRpcReplyCounter: cachedLateRpcReplyCounter,\n    };\n  }\n\n  const api = tryLoadOpenTelemetryApi();\n  if (!api) {\n    return {\n      publishCounter: undefined,\n      consumeCounter: undefined,\n      publishLatencyHistogram: undefined,\n      consumeLatencyHistogram: undefined,\n      lateRpcReplyCounter: undefined,\n    };\n  }\n\n  const meter = api.metrics.getMeter(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION);\n\n  cachedPublishCounter = meter.createCounter(\"amqp.client.messages.published\", {\n    description: \"Number of messages published to AMQP broker\",\n    unit: \"{message}\",\n  });\n\n  cachedConsumeCounter = meter.createCounter(\"amqp.worker.messages.consumed\", {\n    description: \"Number of messages consumed from AMQP broker\",\n    unit: \"{message}\",\n  });\n\n  cachedPublishLatencyHistogram = meter.createHistogram(\"amqp.client.publish.duration\", {\n    description: \"Duration of message publish operations\",\n    unit: \"ms\",\n  });\n\n  cachedConsumeLatencyHistogram = meter.createHistogram(\"amqp.worker.process.duration\", {\n    description: \"Duration of message processing operations\",\n    unit: \"ms\",\n  });\n\n  cachedLateRpcReplyCounter = meter.createCounter(\"amqp.client.rpc.late_reply\", {\n    description:\n      \"RPC replies received after the caller stopped waiting (timeout, cancellation, or unknown correlationId)\",\n    unit: \"{message}\",\n  });\n\n  return {\n    publishCounter: cachedPublishCounter,\n    consumeCounter: cachedConsumeCounter,\n    publishLatencyHistogram: cachedPublishLatencyHistogram,\n    consumeLatencyHistogram: cachedConsumeLatencyHistogram,\n    lateRpcReplyCounter: cachedLateRpcReplyCounter,\n  };\n}\n\n/**\n * Default telemetry provider that uses OpenTelemetry API if available.\n */\nexport const defaultTelemetryProvider: TelemetryProvider = {\n  getTracer,\n  getPublishCounter: () => getMeterInstruments().publishCounter,\n  getConsumeCounter: () => getMeterInstruments().consumeCounter,\n  getPublishLatencyHistogram: () => getMeterInstruments().publishLatencyHistogram,\n  getConsumeLatencyHistogram: () => getMeterInstruments().consumeLatencyHistogram,\n  getLateRpcReplyCounter: () => getMeterInstruments().lateRpcReplyCounter,\n};\n\n/**\n * Create a span for a publish operation.\n * Returns undefined if OpenTelemetry is not available.\n */\nexport function startPublishSpan(\n  provider: TelemetryProvider,\n  exchangeName: string,\n  routingKey: string | undefined,\n  attributes?: Attributes,\n): Span | undefined {\n  const tracer = provider.getTracer();\n  if (!tracer) {\n    return undefined;\n  }\n\n  const spanName = `${exchangeName} publish`;\n\n  return tracer.startSpan(spanName, {\n    kind: SpanKind.PRODUCER,\n    attributes: {\n      [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n        MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n      [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,\n      [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]:\n        MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_EXCHANGE,\n      [MessagingSemanticConventions.MESSAGING_OPERATION]:\n        MessagingSemanticConventions.MESSAGING_OPERATION_PUBLISH,\n      ...(routingKey\n        ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey }\n        : {}),\n      ...attributes,\n    },\n  });\n}\n\n/**\n * Create a span for a consume/process operation.\n * Returns undefined if OpenTelemetry is not available.\n */\nexport function startConsumeSpan(\n  provider: TelemetryProvider,\n  queueName: string,\n  consumerName: string,\n  attributes?: Attributes,\n): Span | undefined {\n  const tracer = provider.getTracer();\n  if (!tracer) {\n    return undefined;\n  }\n\n  const spanName = `${queueName} process`;\n\n  return tracer.startSpan(spanName, {\n    kind: SpanKind.CONSUMER,\n    attributes: {\n      [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n        MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n      [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,\n      [MessagingSemanticConventions.MESSAGING_DESTINATION_KIND]:\n        MessagingSemanticConventions.MESSAGING_DESTINATION_KIND_QUEUE,\n      [MessagingSemanticConventions.MESSAGING_OPERATION]:\n        MessagingSemanticConventions.MESSAGING_OPERATION_PROCESS,\n      [MessagingSemanticConventions.AMQP_CONSUMER_NAME]: consumerName,\n      ...attributes,\n    },\n  });\n}\n\n/**\n * End a span with success status.\n */\nexport function endSpanSuccess(span: Span | undefined): void {\n  if (!span) {\n    return;\n  }\n\n  const api = tryLoadOpenTelemetryApi();\n  if (api) {\n    span.setStatus({ code: api.SpanStatusCode.OK });\n  }\n  span.end();\n}\n\n/**\n * End a span with error status.\n */\nexport function endSpanError(span: Span | undefined, error: Error): void {\n  if (!span) {\n    return;\n  }\n\n  const api = tryLoadOpenTelemetryApi();\n  if (api) {\n    span.setStatus({ code: api.SpanStatusCode.ERROR, message: error.message });\n    span.recordException(error);\n    span.setAttribute(MessagingSemanticConventions.ERROR_TYPE, error.name);\n  }\n  span.end();\n}\n\n/**\n * Record a publish metric.\n */\nexport function recordPublishMetric(\n  provider: TelemetryProvider,\n  exchangeName: string,\n  routingKey: string | undefined,\n  success: boolean,\n  durationMs: number,\n): void {\n  const publishCounter = provider.getPublishCounter();\n  const publishLatencyHistogram = provider.getPublishLatencyHistogram();\n\n  const attributes: Attributes = {\n    [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n      MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n    [MessagingSemanticConventions.MESSAGING_DESTINATION]: exchangeName,\n    ...(routingKey\n      ? { [MessagingSemanticConventions.MESSAGING_RABBITMQ_ROUTING_KEY]: routingKey }\n      : {}),\n    success: success,\n  };\n\n  publishCounter?.add(1, attributes);\n  publishLatencyHistogram?.record(durationMs, attributes);\n}\n\n/**\n * Record a consume metric.\n */\nexport function recordConsumeMetric(\n  provider: TelemetryProvider,\n  queueName: string,\n  consumerName: string,\n  success: boolean,\n  durationMs: number,\n): void {\n  const consumeCounter = provider.getConsumeCounter();\n  const consumeLatencyHistogram = provider.getConsumeLatencyHistogram();\n\n  const attributes: Attributes = {\n    [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n      MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n    [MessagingSemanticConventions.MESSAGING_DESTINATION]: queueName,\n    [MessagingSemanticConventions.AMQP_CONSUMER_NAME]: consumerName,\n    success: success,\n  };\n\n  consumeCounter?.add(1, attributes);\n  consumeLatencyHistogram?.record(durationMs, attributes);\n}\n\n/**\n * Record an RPC reply that arrived after the caller stopped waiting.\n *\n * @param reason - Why the reply was orphaned. `\"unknown-correlation-id\"` is\n *   the typical \"caller already timed out\" case; `\"missing-correlation-id\"`\n *   means the broker delivered a reply with no correlationId at all (a\n *   protocol violation by the responder).\n */\nexport function recordLateRpcReply(\n  provider: TelemetryProvider,\n  reason: \"unknown-correlation-id\" | \"missing-correlation-id\",\n): void {\n  const counter = provider.getLateRpcReplyCounter();\n\n  const attributes: Attributes = {\n    [MessagingSemanticConventions.MESSAGING_SYSTEM]:\n      MessagingSemanticConventions.MESSAGING_SYSTEM_RABBITMQ,\n    reason,\n  };\n\n  counter?.add(1, attributes);\n}\n\n/**\n * Reset the cached OpenTelemetry API module and instruments.\n * For testing purposes only.\n * @internal\n */\nexport function _internal_resetTelemetryCache(): void {\n  otelApi = undefined;\n  cachedTracer = undefined;\n  cachedPublishCounter = undefined;\n  cachedConsumeCounter = undefined;\n  cachedPublishLatencyHistogram = undefined;\n  cachedConsumeLatencyHistogram = undefined;\n  cachedLateRpcReplyCounter = undefined;\n}\n\n/** @deprecated Renamed to {@link _internal_resetTelemetryCache} per the org `_internal_` convention. */\nexport function _resetTelemetryCacheForTesting(): void {\n  _internal_resetTelemetryCache();\n}\n"],"mappings":"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"}