@amqp-contract/core 1.0.0 → 2.0.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/dist/index.cjs +4 -4
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +5 -5
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +71 -71
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -439,7 +439,7 @@ var AmqpClient = class {
|
|
|
439
439
|
* Wait for the channel to be connected and ready.
|
|
440
440
|
*
|
|
441
441
|
* If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
442
|
-
* AsyncResult resolves to `
|
|
442
|
+
* AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses.
|
|
443
443
|
* Without a timeout, this waits forever — amqp-connection-manager retries
|
|
444
444
|
* connections indefinitely and never errors on its own.
|
|
445
445
|
*
|
|
@@ -501,7 +501,7 @@ var AmqpClient = class {
|
|
|
501
501
|
consume(queue, callback, options) {
|
|
502
502
|
const { prefetch, ...consumeOptions } = options ?? {};
|
|
503
503
|
if (prefetch !== void 0) {
|
|
504
|
-
if (!Number.isInteger(prefetch) || prefetch < 0 || prefetch > 65535) return (0, unthrown.
|
|
504
|
+
if (!Number.isInteger(prefetch) || prefetch < 0 || prefetch > 65535) return (0, unthrown.Err)(new TechnicalError(`Invalid prefetch: expected a non-negative integer ≤ 65535, got ${String(prefetch)}`)).toAsync();
|
|
505
505
|
}
|
|
506
506
|
const prefetchSetup = typeof prefetch === "number" ? async (channel) => {
|
|
507
507
|
await channel.prefetch(prefetch, false);
|
|
@@ -591,10 +591,10 @@ var AmqpClient = class {
|
|
|
591
591
|
return (0, unthrown.fromSafePromise)((async () => {
|
|
592
592
|
const channelResult = await (0, unthrown.fromPromise)(this.channelWrapper.close(), (error) => new TechnicalError("Failed to close channel", error));
|
|
593
593
|
const releaseResult = await (0, unthrown.fromPromise)(ConnectionManagerSingleton.getInstance().releaseConnection(this.urls, this.connectionOptions), (error) => new TechnicalError("Failed to release connection", error));
|
|
594
|
-
if (channelResult.isErr() && releaseResult.isErr()) return (0, unthrown.
|
|
594
|
+
if (channelResult.isErr() && releaseResult.isErr()) return (0, unthrown.Err)(new TechnicalError("Failed to close channel and release connection", new AggregateError([channelResult.error, releaseResult.error], "Failed to close channel and release connection")));
|
|
595
595
|
if (channelResult.isErr()) return channelResult;
|
|
596
596
|
if (releaseResult.isErr()) return releaseResult;
|
|
597
|
-
return (0, unthrown.
|
|
597
|
+
return (0, unthrown.Ok)(void 0);
|
|
598
598
|
})()).flatMap((result) => result);
|
|
599
599
|
}
|
|
600
600
|
/**
|
package/dist/index.d.cts
CHANGED
|
@@ -173,7 +173,7 @@ declare class AmqpClient {
|
|
|
173
173
|
* Wait for the channel to be connected and ready.
|
|
174
174
|
*
|
|
175
175
|
* If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
176
|
-
* AsyncResult resolves to `
|
|
176
|
+
* AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses.
|
|
177
177
|
* Without a timeout, this waits forever — amqp-connection-manager retries
|
|
178
178
|
* connections indefinitely and never errors on its own.
|
|
179
179
|
*
|
package/dist/index.d.mts
CHANGED
|
@@ -173,7 +173,7 @@ declare class AmqpClient {
|
|
|
173
173
|
* Wait for the channel to be connected and ready.
|
|
174
174
|
*
|
|
175
175
|
* If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
176
|
-
* AsyncResult resolves to `
|
|
176
|
+
* AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses.
|
|
177
177
|
* Without a timeout, this waits forever — amqp-connection-manager retries
|
|
178
178
|
* connections indefinitely and never errors on its own.
|
|
179
179
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import {
|
|
2
|
+
import { Err, Ok, TaggedError, fromPromise, fromSafePromise, fromThrowable } from "unthrown";
|
|
3
3
|
import amqp from "amqp-connection-manager";
|
|
4
4
|
import { extractQueue } from "@amqp-contract/contract";
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
@@ -418,7 +418,7 @@ var AmqpClient = class {
|
|
|
418
418
|
* Wait for the channel to be connected and ready.
|
|
419
419
|
*
|
|
420
420
|
* If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
421
|
-
* AsyncResult resolves to `
|
|
421
|
+
* AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses.
|
|
422
422
|
* Without a timeout, this waits forever — amqp-connection-manager retries
|
|
423
423
|
* connections indefinitely and never errors on its own.
|
|
424
424
|
*
|
|
@@ -480,7 +480,7 @@ var AmqpClient = class {
|
|
|
480
480
|
consume(queue, callback, options) {
|
|
481
481
|
const { prefetch, ...consumeOptions } = options ?? {};
|
|
482
482
|
if (prefetch !== void 0) {
|
|
483
|
-
if (!Number.isInteger(prefetch) || prefetch < 0 || prefetch > 65535) return
|
|
483
|
+
if (!Number.isInteger(prefetch) || prefetch < 0 || prefetch > 65535) return Err(new TechnicalError(`Invalid prefetch: expected a non-negative integer ≤ 65535, got ${String(prefetch)}`)).toAsync();
|
|
484
484
|
}
|
|
485
485
|
const prefetchSetup = typeof prefetch === "number" ? async (channel) => {
|
|
486
486
|
await channel.prefetch(prefetch, false);
|
|
@@ -570,10 +570,10 @@ var AmqpClient = class {
|
|
|
570
570
|
return fromSafePromise((async () => {
|
|
571
571
|
const channelResult = await fromPromise(this.channelWrapper.close(), (error) => new TechnicalError("Failed to close channel", error));
|
|
572
572
|
const releaseResult = await fromPromise(ConnectionManagerSingleton.getInstance().releaseConnection(this.urls, this.connectionOptions), (error) => new TechnicalError("Failed to release connection", error));
|
|
573
|
-
if (channelResult.isErr() && releaseResult.isErr()) return
|
|
573
|
+
if (channelResult.isErr() && releaseResult.isErr()) return Err(new TechnicalError("Failed to close channel and release connection", new AggregateError([channelResult.error, releaseResult.error], "Failed to close channel and release connection")));
|
|
574
574
|
if (channelResult.isErr()) return channelResult;
|
|
575
575
|
if (releaseResult.isErr()) return releaseResult;
|
|
576
|
-
return
|
|
576
|
+
return Ok(void 0);
|
|
577
577
|
})()).flatMap((result) => result);
|
|
578
578
|
}
|
|
579
579
|
/**
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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 _getConnectionCountForTesting(): number {\n return ConnectionManagerSingleton.getInstance()._getConnectionCountForTesting();\n}\n\n/**\n * Close every pooled connection and clear ref-counts. Test-only helper.\n *\n * @internal\n */\nexport function _resetConnectionsForTesting(): Promise<void> {\n return ConnectionManagerSingleton.getInstance()._resetForTesting();\n}\n","import { 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 message: string;\n cause?: unknown;\n}> {\n constructor(message: string, cause?: unknown) {\n super({ message, cause });\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 message: string;\n source: string;\n issues: unknown;\n}> {\n constructor(source: string, issues: unknown) {\n super({ message: `Message validation failed for \"${source}\"`, source, issues });\n }\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 { err, fromPromise, fromSafePromise, ok, type AsyncResult, type Result } 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) => new TechnicalError(\"Failed to connect to AMQP broker\", error),\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) => new TechnicalError(\"Failed to publish message\", error),\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) => new TechnicalError(\"Failed to publish message to 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 err(\n new TechnicalError(\n `Invalid prefetch: expected a non-negative integer ≤ 65535, got ${String(prefetch)}`,\n ),\n ).toAsync();\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 _resetTelemetryCacheForTesting(): void {\n otelApi = undefined;\n cachedTracer = undefined;\n cachedPublishCounter = undefined;\n cachedConsumeCounter = undefined;\n cachedPublishLatencyHistogram = undefined;\n cachedConsumeLatencyHistogram = undefined;\n cachedLateRpcReplyCounter = undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"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 _getConnectionCountForTesting(): number {\n return ConnectionManagerSingleton.getInstance()._getConnectionCountForTesting();\n}\n\n/**\n * Close every pooled connection and clear ref-counts. Test-only helper.\n *\n * @internal\n */\nexport function _resetConnectionsForTesting(): Promise<void> {\n return ConnectionManagerSingleton.getInstance()._resetForTesting();\n}\n","import { 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 message: string;\n cause?: unknown;\n}> {\n constructor(message: string, cause?: unknown) {\n super({ message, cause });\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 message: string;\n source: string;\n issues: unknown;\n}> {\n constructor(source: string, issues: unknown) {\n super({ message: `Message validation failed for \"${source}\"`, source, issues });\n }\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 { Err, fromPromise, fromSafePromise, Ok, type AsyncResult, type Result } 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) => new TechnicalError(\"Failed to connect to AMQP broker\", error),\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) => new TechnicalError(\"Failed to publish message\", error),\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) => new TechnicalError(\"Failed to publish message to 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 Err(\n new TechnicalError(\n `Invalid prefetch: expected a non-negative integer ≤ 65535, got ${String(prefetch)}`,\n ),\n ).toAsync();\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 _resetTelemetryCacheForTesting(): void {\n otelApi = undefined;\n cachedTracer = undefined;\n cachedPublishCounter = undefined;\n cachedConsumeCounter = undefined;\n cachedPublishLatencyHistogram = undefined;\n cachedConsumeLatencyHistogram = undefined;\n cachedLateRpcReplyCounter = undefined;\n}\n"],"mappings":"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"}
|
package/docs/index.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
### AmqpClient
|
|
10
10
|
|
|
11
|
-
Defined in: [packages/core/src/amqp-client.ts:141](https://github.com/btravstack/amqp-contract/blob/
|
|
11
|
+
Defined in: [packages/core/src/amqp-client.ts:141](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L141)
|
|
12
12
|
|
|
13
13
|
AMQP client that manages connections and channels with automatic topology setup.
|
|
14
14
|
|
|
@@ -46,7 +46,7 @@ await client.close();
|
|
|
46
46
|
new AmqpClient(contract, options): AmqpClient;
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Defined in: [packages/core/src/amqp-client.ts:169](https://github.com/btravstack/amqp-contract/blob/
|
|
49
|
+
Defined in: [packages/core/src/amqp-client.ts:169](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L169)
|
|
50
50
|
|
|
51
51
|
Create a new AMQP client instance.
|
|
52
52
|
|
|
@@ -74,7 +74,7 @@ The client will automatically:
|
|
|
74
74
|
ack(msg, allUpTo?): void;
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Defined in: [packages/core/src/amqp-client.ts:426](https://github.com/btravstack/amqp-contract/blob/
|
|
77
|
+
Defined in: [packages/core/src/amqp-client.ts:426](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L426)
|
|
78
78
|
|
|
79
79
|
Acknowledge a message.
|
|
80
80
|
|
|
@@ -95,7 +95,7 @@ Acknowledge a message.
|
|
|
95
95
|
addSetup(setup): void;
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
Defined in: [packages/core/src/amqp-client.ts:448](https://github.com/btravstack/amqp-contract/blob/
|
|
98
|
+
Defined in: [packages/core/src/amqp-client.ts:448](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L448)
|
|
99
99
|
|
|
100
100
|
Add a setup function to be called when the channel is created or reconnected.
|
|
101
101
|
|
|
@@ -117,7 +117,7 @@ This is useful for setting up channel-level configuration like prefetch.
|
|
|
117
117
|
cancel(consumerTag): AsyncResult<void, TechnicalError>;
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
Defined in: [packages/core/src/amqp-client.ts:395](https://github.com/btravstack/amqp-contract/blob/
|
|
120
|
+
Defined in: [packages/core/src/amqp-client.ts:395](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L395)
|
|
121
121
|
|
|
122
122
|
Cancel a consumer by its consumer tag.
|
|
123
123
|
|
|
@@ -137,7 +137,7 @@ Cancel a consumer by its consumer tag.
|
|
|
137
137
|
close(): AsyncResult<void, TechnicalError>;
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
Defined in: [packages/core/src/amqp-client.ts:478](https://github.com/btravstack/amqp-contract/blob/
|
|
140
|
+
Defined in: [packages/core/src/amqp-client.ts:478](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L478)
|
|
141
141
|
|
|
142
142
|
Close the channel and release the connection reference.
|
|
143
143
|
|
|
@@ -162,7 +162,7 @@ consume(
|
|
|
162
162
|
options?): AsyncResult<string, TechnicalError>;
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
Defined in: [packages/core/src/amqp-client.ts:321](https://github.com/btravstack/amqp-contract/blob/
|
|
165
|
+
Defined in: [packages/core/src/amqp-client.ts:321](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L321)
|
|
166
166
|
|
|
167
167
|
Start consuming messages from a queue.
|
|
168
168
|
|
|
@@ -198,7 +198,7 @@ AsyncResult resolving to the consumer tag.
|
|
|
198
198
|
getConnection(): IAmqpConnectionManager;
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
Defined in: [packages/core/src/amqp-client.ts:224](https://github.com/btravstack/amqp-contract/blob/
|
|
201
|
+
Defined in: [packages/core/src/amqp-client.ts:224](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L224)
|
|
202
202
|
|
|
203
203
|
Get the underlying connection manager
|
|
204
204
|
|
|
@@ -221,7 +221,7 @@ nack(
|
|
|
221
221
|
requeue?): void;
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
Defined in: [packages/core/src/amqp-client.ts:437](https://github.com/btravstack/amqp-contract/blob/
|
|
224
|
+
Defined in: [packages/core/src/amqp-client.ts:437](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L437)
|
|
225
225
|
|
|
226
226
|
Negative acknowledge a message.
|
|
227
227
|
|
|
@@ -243,7 +243,7 @@ Negative acknowledge a message.
|
|
|
243
243
|
on(event, listener): void;
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
Defined in: [packages/core/src/amqp-client.ts:463](https://github.com/btravstack/amqp-contract/blob/
|
|
246
|
+
Defined in: [packages/core/src/amqp-client.ts:463](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L463)
|
|
247
247
|
|
|
248
248
|
Register an event listener on the channel wrapper.
|
|
249
249
|
|
|
@@ -273,7 +273,7 @@ publish(
|
|
|
273
273
|
options?): AsyncResult<boolean, TechnicalError>;
|
|
274
274
|
```
|
|
275
275
|
|
|
276
|
-
Defined in: [packages/core/src/amqp-client.ts:276](https://github.com/btravstack/amqp-contract/blob/
|
|
276
|
+
Defined in: [packages/core/src/amqp-client.ts:276](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L276)
|
|
277
277
|
|
|
278
278
|
Publish a message to an exchange.
|
|
279
279
|
|
|
@@ -301,7 +301,7 @@ sendToQueue(
|
|
|
301
301
|
options?): AsyncResult<boolean, TechnicalError>;
|
|
302
302
|
```
|
|
303
303
|
|
|
304
|
-
Defined in: [packages/core/src/amqp-client.ts:293](https://github.com/btravstack/amqp-contract/blob/
|
|
304
|
+
Defined in: [packages/core/src/amqp-client.ts:293](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L293)
|
|
305
305
|
|
|
306
306
|
Publish a message directly to a queue.
|
|
307
307
|
|
|
@@ -325,12 +325,12 @@ AsyncResult resolving to `true` if the message was sent, `false` if the channel
|
|
|
325
325
|
waitForConnect(): AsyncResult<void, TechnicalError>;
|
|
326
326
|
```
|
|
327
327
|
|
|
328
|
-
Defined in: [packages/core/src/amqp-client.ts:242](https://github.com/btravstack/amqp-contract/blob/
|
|
328
|
+
Defined in: [packages/core/src/amqp-client.ts:242](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L242)
|
|
329
329
|
|
|
330
330
|
Wait for the channel to be connected and ready.
|
|
331
331
|
|
|
332
332
|
If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
333
|
-
AsyncResult resolves to `
|
|
333
|
+
AsyncResult resolves to `Err(TechnicalError)` once the timeout elapses.
|
|
334
334
|
Without a timeout, this waits forever — amqp-connection-manager retries
|
|
335
335
|
connections indefinitely and never errors on its own.
|
|
336
336
|
|
|
@@ -348,7 +348,7 @@ automatically. The typed factories handle this cleanup for you.
|
|
|
348
348
|
|
|
349
349
|
### MessageValidationError
|
|
350
350
|
|
|
351
|
-
Defined in: [packages/core/src/errors.ts:38](https://github.com/btravstack/amqp-contract/blob/
|
|
351
|
+
Defined in: [packages/core/src/errors.ts:38](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/errors.ts#L38)
|
|
352
352
|
|
|
353
353
|
Error thrown when message validation fails (payload or headers).
|
|
354
354
|
|
|
@@ -385,7 +385,7 @@ The validation issues from the Standard Schema validation
|
|
|
385
385
|
new MessageValidationError(source, issues): MessageValidationError;
|
|
386
386
|
```
|
|
387
387
|
|
|
388
|
-
Defined in: [packages/core/src/errors.ts:45](https://github.com/btravstack/amqp-contract/blob/
|
|
388
|
+
Defined in: [packages/core/src/errors.ts:45](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/errors.ts#L45)
|
|
389
389
|
|
|
390
390
|
###### Parameters
|
|
391
391
|
|
|
@@ -414,19 +414,19 @@ TaggedError("@amqp-contract/MessageValidationError", {
|
|
|
414
414
|
|
|
415
415
|
| Property | Modifier | Type | Inherited from | Defined in |
|
|
416
416
|
| ------ | ------ | ------ | ------ | ------ |
|
|
417
|
-
| <a id="_tag"></a> `_tag` | `readonly` | `"@amqp-contract/MessageValidationError"` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", })._tag` | node\_modules/.pnpm/unthrown@0.
|
|
417
|
+
| <a id="_tag"></a> `_tag` | `readonly` | `"@amqp-contract/MessageValidationError"` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", })._tag` | node\_modules/.pnpm/unthrown@1.0.0/node\_modules/unthrown/dist/index.d.mts:778 |
|
|
418
418
|
| <a id="cause"></a> `cause?` | `public` | `unknown` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).cause` | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:24 |
|
|
419
|
-
| <a id="issues"></a> `issues` | `readonly` | `unknown` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).issues` | [packages/core/src/errors.ts:43](https://github.com/btravstack/amqp-contract/blob/
|
|
419
|
+
| <a id="issues"></a> `issues` | `readonly` | `unknown` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).issues` | [packages/core/src/errors.ts:43](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/errors.ts#L43) |
|
|
420
420
|
| <a id="message"></a> `message` | `public` | `string` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).message` | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es5.d.ts:1075 |
|
|
421
421
|
| <a id="name"></a> `name` | `public` | `string` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).name` | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es5.d.ts:1074 |
|
|
422
|
-
| <a id="source"></a> `source` | `readonly` | `string` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).source` | [packages/core/src/errors.ts:42](https://github.com/btravstack/amqp-contract/blob/
|
|
422
|
+
| <a id="source"></a> `source` | `readonly` | `string` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).source` | [packages/core/src/errors.ts:42](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/errors.ts#L42) |
|
|
423
423
|
| <a id="stack"></a> `stack?` | `public` | `string` | `TaggedError("@amqp-contract/MessageValidationError", { name: "MessageValidationError", }).stack` | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 |
|
|
424
424
|
|
|
425
425
|
***
|
|
426
426
|
|
|
427
427
|
### TechnicalError
|
|
428
428
|
|
|
429
|
-
Defined in: [packages/core/src/errors.ts:16](https://github.com/btravstack/amqp-contract/blob/
|
|
429
|
+
Defined in: [packages/core/src/errors.ts:16](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/errors.ts#L16)
|
|
430
430
|
|
|
431
431
|
Error for technical/runtime failures that cannot be prevented by TypeScript.
|
|
432
432
|
|
|
@@ -455,7 +455,7 @@ a `Result`, never the `Defect` channel).
|
|
|
455
455
|
new TechnicalError(message, cause?): TechnicalError;
|
|
456
456
|
```
|
|
457
457
|
|
|
458
|
-
Defined in: [packages/core/src/errors.ts:22](https://github.com/btravstack/amqp-contract/blob/
|
|
458
|
+
Defined in: [packages/core/src/errors.ts:22](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/errors.ts#L22)
|
|
459
459
|
|
|
460
460
|
###### Parameters
|
|
461
461
|
|
|
@@ -483,7 +483,7 @@ TaggedError("@amqp-contract/TechnicalError", {
|
|
|
483
483
|
|
|
484
484
|
| Property | Modifier | Type | Inherited from | Defined in |
|
|
485
485
|
| ------ | ------ | ------ | ------ | ------ |
|
|
486
|
-
| <a id="_tag-1"></a> `_tag` | `readonly` | `"@amqp-contract/TechnicalError"` | `TaggedError("@amqp-contract/TechnicalError", { name: "TechnicalError", })._tag` | node\_modules/.pnpm/unthrown@0.
|
|
486
|
+
| <a id="_tag-1"></a> `_tag` | `readonly` | `"@amqp-contract/TechnicalError"` | `TaggedError("@amqp-contract/TechnicalError", { name: "TechnicalError", })._tag` | node\_modules/.pnpm/unthrown@1.0.0/node\_modules/unthrown/dist/index.d.mts:778 |
|
|
487
487
|
| <a id="cause-1"></a> `cause?` | `public` | `unknown` | [`MessageValidationError`](#messagevalidationerror).[`cause`](#cause) | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:24 |
|
|
488
488
|
| <a id="message-1"></a> `message` | `public` | `string` | `TaggedError("@amqp-contract/TechnicalError", { name: "TechnicalError", }).message` | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es5.d.ts:1075 |
|
|
489
489
|
| <a id="name-1"></a> `name` | `public` | `string` | `TaggedError("@amqp-contract/TechnicalError", { name: "TechnicalError", }).name` | node\_modules/.pnpm/typescript@6.0.3/node\_modules/typescript/lib/lib.es5.d.ts:1074 |
|
|
@@ -497,7 +497,7 @@ TaggedError("@amqp-contract/TechnicalError", {
|
|
|
497
497
|
type AmqpClientOptions = object;
|
|
498
498
|
```
|
|
499
499
|
|
|
500
|
-
Defined in: [packages/core/src/amqp-client.ts:73](https://github.com/btravstack/amqp-contract/blob/
|
|
500
|
+
Defined in: [packages/core/src/amqp-client.ts:73](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L73)
|
|
501
501
|
|
|
502
502
|
Options for creating an AMQP client.
|
|
503
503
|
|
|
@@ -505,10 +505,10 @@ Options for creating an AMQP client.
|
|
|
505
505
|
|
|
506
506
|
| Property | Type | Description | Defined in |
|
|
507
507
|
| ------ | ------ | ------ | ------ |
|
|
508
|
-
| <a id="channeloptions"></a> `channelOptions?` | `Partial`<`CreateChannelOpts`> | Optional channel configuration options. | [packages/core/src/amqp-client.ts:76](https://github.com/btravstack/amqp-contract/blob/
|
|
509
|
-
| <a id="connectionoptions"></a> `connectionOptions?` | `AmqpConnectionManagerOptions` | Optional connection configuration (heartbeat, reconnect settings, etc.). | [packages/core/src/amqp-client.ts:75](https://github.com/btravstack/amqp-contract/blob/
|
|
510
|
-
| <a id="connecttimeoutms"></a> `connectTimeoutMs?` | `number` \| `null` | Maximum time in ms to wait for the channel to become ready in `waitForConnect`. Defaults to [DEFAULT\_CONNECT\_TIMEOUT\_MS](#default_connect_timeout_ms). Pass `null` to disable the timeout entirely (amqp-connection-manager will retry indefinitely). | [packages/core/src/amqp-client.ts:77](https://github.com/btravstack/amqp-contract/blob/
|
|
511
|
-
| <a id="urls"></a> `urls` | `ConnectionUrl`[] | AMQP broker URL(s). Multiple URLs provide failover support. | [packages/core/src/amqp-client.ts:74](https://github.com/btravstack/amqp-contract/blob/
|
|
508
|
+
| <a id="channeloptions"></a> `channelOptions?` | `Partial`<`CreateChannelOpts`> | Optional channel configuration options. | [packages/core/src/amqp-client.ts:76](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L76) |
|
|
509
|
+
| <a id="connectionoptions"></a> `connectionOptions?` | `AmqpConnectionManagerOptions` | Optional connection configuration (heartbeat, reconnect settings, etc.). | [packages/core/src/amqp-client.ts:75](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L75) |
|
|
510
|
+
| <a id="connecttimeoutms"></a> `connectTimeoutMs?` | `number` \| `null` | Maximum time in ms to wait for the channel to become ready in `waitForConnect`. Defaults to [DEFAULT\_CONNECT\_TIMEOUT\_MS](#default_connect_timeout_ms). Pass `null` to disable the timeout entirely (amqp-connection-manager will retry indefinitely). | [packages/core/src/amqp-client.ts:77](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L77) |
|
|
511
|
+
| <a id="urls"></a> `urls` | `ConnectionUrl`[] | AMQP broker URL(s). Multiple URLs provide failover support. | [packages/core/src/amqp-client.ts:74](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L74) |
|
|
512
512
|
|
|
513
513
|
***
|
|
514
514
|
|
|
@@ -518,7 +518,7 @@ Options for creating an AMQP client.
|
|
|
518
518
|
type ConsumeCallback = (msg) => void | Promise<void>;
|
|
519
519
|
```
|
|
520
520
|
|
|
521
|
-
Defined in: [packages/core/src/amqp-client.ts:83](https://github.com/btravstack/amqp-contract/blob/
|
|
521
|
+
Defined in: [packages/core/src/amqp-client.ts:83](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L83)
|
|
522
522
|
|
|
523
523
|
Callback type for consuming messages.
|
|
524
524
|
|
|
@@ -540,7 +540,7 @@ Callback type for consuming messages.
|
|
|
540
540
|
type ConsumerOptions = Options.Consume & object;
|
|
541
541
|
```
|
|
542
542
|
|
|
543
|
-
Defined in: [packages/core/src/amqp-client.ts:108](https://github.com/btravstack/amqp-contract/blob/
|
|
543
|
+
Defined in: [packages/core/src/amqp-client.ts:108](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L108)
|
|
544
544
|
|
|
545
545
|
Consume options that extend amqplib's `Options.Consume` with an optional
|
|
546
546
|
per-consumer prefetch count.
|
|
@@ -556,7 +556,7 @@ automatically on channel reconnect.
|
|
|
556
556
|
|
|
557
557
|
| Name | Type | Description | Defined in |
|
|
558
558
|
| ------ | ------ | ------ | ------ |
|
|
559
|
-
| `prefetch?` | `number` | Per-consumer prefetch count. Applied before `channel.consume(...)`. | [packages/core/src/amqp-client.ts:110](https://github.com/btravstack/amqp-contract/blob/
|
|
559
|
+
| `prefetch?` | `number` | Per-consumer prefetch count. Applied before `channel.consume(...)`. | [packages/core/src/amqp-client.ts:110](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L110) |
|
|
560
560
|
|
|
561
561
|
***
|
|
562
562
|
|
|
@@ -566,7 +566,7 @@ automatically on channel reconnect.
|
|
|
566
566
|
type Logger = object;
|
|
567
567
|
```
|
|
568
568
|
|
|
569
|
-
Defined in: [packages/core/src/logger.ts:30](https://github.com/btravstack/amqp-contract/blob/
|
|
569
|
+
Defined in: [packages/core/src/logger.ts:30](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L30)
|
|
570
570
|
|
|
571
571
|
Logger interface for amqp-contract packages.
|
|
572
572
|
|
|
@@ -593,7 +593,7 @@ const logger: Logger = {
|
|
|
593
593
|
debug(message, context?): void;
|
|
594
594
|
```
|
|
595
595
|
|
|
596
|
-
Defined in: [packages/core/src/logger.ts:36](https://github.com/btravstack/amqp-contract/blob/
|
|
596
|
+
Defined in: [packages/core/src/logger.ts:36](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L36)
|
|
597
597
|
|
|
598
598
|
Log debug level messages
|
|
599
599
|
|
|
@@ -614,7 +614,7 @@ Log debug level messages
|
|
|
614
614
|
error(message, context?): void;
|
|
615
615
|
```
|
|
616
616
|
|
|
617
|
-
Defined in: [packages/core/src/logger.ts:57](https://github.com/btravstack/amqp-contract/blob/
|
|
617
|
+
Defined in: [packages/core/src/logger.ts:57](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L57)
|
|
618
618
|
|
|
619
619
|
Log error level messages
|
|
620
620
|
|
|
@@ -635,7 +635,7 @@ Log error level messages
|
|
|
635
635
|
info(message, context?): void;
|
|
636
636
|
```
|
|
637
637
|
|
|
638
|
-
Defined in: [packages/core/src/logger.ts:43](https://github.com/btravstack/amqp-contract/blob/
|
|
638
|
+
Defined in: [packages/core/src/logger.ts:43](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L43)
|
|
639
639
|
|
|
640
640
|
Log info level messages
|
|
641
641
|
|
|
@@ -656,7 +656,7 @@ Log info level messages
|
|
|
656
656
|
warn(message, context?): void;
|
|
657
657
|
```
|
|
658
658
|
|
|
659
|
-
Defined in: [packages/core/src/logger.ts:50](https://github.com/btravstack/amqp-contract/blob/
|
|
659
|
+
Defined in: [packages/core/src/logger.ts:50](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L50)
|
|
660
660
|
|
|
661
661
|
Log warning level messages
|
|
662
662
|
|
|
@@ -679,7 +679,7 @@ Log warning level messages
|
|
|
679
679
|
type LoggerContext = Record<string, unknown> & object;
|
|
680
680
|
```
|
|
681
681
|
|
|
682
|
-
Defined in: [packages/core/src/logger.ts:9](https://github.com/btravstack/amqp-contract/blob/
|
|
682
|
+
Defined in: [packages/core/src/logger.ts:9](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L9)
|
|
683
683
|
|
|
684
684
|
Context object for logger methods.
|
|
685
685
|
|
|
@@ -690,7 +690,7 @@ for common logging context properties.
|
|
|
690
690
|
|
|
691
691
|
| Name | Type | Defined in |
|
|
692
692
|
| ------ | ------ | ------ |
|
|
693
|
-
| `error?` | `unknown` | [packages/core/src/logger.ts:10](https://github.com/btravstack/amqp-contract/blob/
|
|
693
|
+
| `error?` | `unknown` | [packages/core/src/logger.ts:10](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/logger.ts#L10) |
|
|
694
694
|
|
|
695
695
|
***
|
|
696
696
|
|
|
@@ -700,7 +700,7 @@ for common logging context properties.
|
|
|
700
700
|
type PublishOptions = Options.Publish;
|
|
701
701
|
```
|
|
702
702
|
|
|
703
|
-
Defined in: [packages/core/src/amqp-client.ts:95](https://github.com/btravstack/amqp-contract/blob/
|
|
703
|
+
Defined in: [packages/core/src/amqp-client.ts:95](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L95)
|
|
704
704
|
|
|
705
705
|
Publish options for `AmqpClient.publish` / `AmqpClient.sendToQueue`.
|
|
706
706
|
|
|
@@ -719,7 +719,7 @@ creation, not per-publish.)
|
|
|
719
719
|
type TelemetryProvider = object;
|
|
720
720
|
```
|
|
721
721
|
|
|
722
|
-
Defined in: [packages/core/src/telemetry.ts:54](https://github.com/btravstack/amqp-contract/blob/
|
|
722
|
+
Defined in: [packages/core/src/telemetry.ts:54](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L54)
|
|
723
723
|
|
|
724
724
|
Telemetry provider for AMQP operations.
|
|
725
725
|
Uses lazy loading to gracefully handle cases where OpenTelemetry is not installed.
|
|
@@ -728,12 +728,12 @@ Uses lazy loading to gracefully handle cases where OpenTelemetry is not installe
|
|
|
728
728
|
|
|
729
729
|
| Property | Type | Description | Defined in |
|
|
730
730
|
| ------ | ------ | ------ | ------ |
|
|
731
|
-
| <a id="getconsumecounter"></a> `getConsumeCounter` | () => `Counter` \| `undefined` | Get a counter for messages consumed. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:71](https://github.com/btravstack/amqp-contract/blob/
|
|
732
|
-
| <a id="getconsumelatencyhistogram"></a> `getConsumeLatencyHistogram` | () => `Histogram` \| `undefined` | Get a histogram for consume/process latency. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:83](https://github.com/btravstack/amqp-contract/blob/
|
|
733
|
-
| <a id="getlaterpcreplycounter"></a> `getLateRpcReplyCounter` | () => `Counter` \| `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. | [packages/core/src/telemetry.ts:90](https://github.com/btravstack/amqp-contract/blob/
|
|
734
|
-
| <a id="getpublishcounter"></a> `getPublishCounter` | () => `Counter` \| `undefined` | Get a counter for messages published. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:65](https://github.com/btravstack/amqp-contract/blob/
|
|
735
|
-
| <a id="getpublishlatencyhistogram"></a> `getPublishLatencyHistogram` | () => `Histogram` \| `undefined` | Get a histogram for publish latency. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:77](https://github.com/btravstack/amqp-contract/blob/
|
|
736
|
-
| <a id="gettracer"></a> `getTracer` | () => `Tracer` \| `undefined` | Get a tracer instance for creating spans. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:59](https://github.com/btravstack/amqp-contract/blob/
|
|
731
|
+
| <a id="getconsumecounter"></a> `getConsumeCounter` | () => `Counter` \| `undefined` | Get a counter for messages consumed. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:71](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L71) |
|
|
732
|
+
| <a id="getconsumelatencyhistogram"></a> `getConsumeLatencyHistogram` | () => `Histogram` \| `undefined` | Get a histogram for consume/process latency. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:83](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L83) |
|
|
733
|
+
| <a id="getlaterpcreplycounter"></a> `getLateRpcReplyCounter` | () => `Counter` \| `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. | [packages/core/src/telemetry.ts:90](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L90) |
|
|
734
|
+
| <a id="getpublishcounter"></a> `getPublishCounter` | () => `Counter` \| `undefined` | Get a counter for messages published. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:65](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L65) |
|
|
735
|
+
| <a id="getpublishlatencyhistogram"></a> `getPublishLatencyHistogram` | () => `Histogram` \| `undefined` | Get a histogram for publish latency. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:77](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L77) |
|
|
736
|
+
| <a id="gettracer"></a> `getTracer` | () => `Tracer` \| `undefined` | Get a tracer instance for creating spans. Returns undefined if OpenTelemetry is not available. | [packages/core/src/telemetry.ts:59](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L59) |
|
|
737
737
|
|
|
738
738
|
## Variables
|
|
739
739
|
|
|
@@ -743,7 +743,7 @@ Uses lazy loading to gracefully handle cases where OpenTelemetry is not installe
|
|
|
743
743
|
const DEFAULT_CONNECT_TIMEOUT_MS: 30000 = 30_000;
|
|
744
744
|
```
|
|
745
745
|
|
|
746
|
-
Defined in: [packages/core/src/amqp-client.ts:47](https://github.com/btravstack/amqp-contract/blob/
|
|
746
|
+
Defined in: [packages/core/src/amqp-client.ts:47](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/amqp-client.ts#L47)
|
|
747
747
|
|
|
748
748
|
Default time `waitForConnect` will wait for the broker before erroring out.
|
|
749
749
|
Defaulting to a finite value (rather than waiting forever) means a fail-fast
|
|
@@ -761,7 +761,7 @@ delays to ~24.8 days and silently fires near-immediately on `Infinity`.
|
|
|
761
761
|
const defaultTelemetryProvider: TelemetryProvider;
|
|
762
762
|
```
|
|
763
763
|
|
|
764
|
-
Defined in: [packages/core/src/telemetry.ts:229](https://github.com/btravstack/amqp-contract/blob/
|
|
764
|
+
Defined in: [packages/core/src/telemetry.ts:229](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L229)
|
|
765
765
|
|
|
766
766
|
Default telemetry provider that uses OpenTelemetry API if available.
|
|
767
767
|
|
|
@@ -773,7 +773,7 @@ Default telemetry provider that uses OpenTelemetry API if available.
|
|
|
773
773
|
const MessagingSemanticConventions: object;
|
|
774
774
|
```
|
|
775
775
|
|
|
776
|
-
Defined in: [packages/core/src/telemetry.ts:26](https://github.com/btravstack/amqp-contract/blob/
|
|
776
|
+
Defined in: [packages/core/src/telemetry.ts:26](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L26)
|
|
777
777
|
|
|
778
778
|
Semantic conventions for AMQP messaging following OpenTelemetry standards.
|
|
779
779
|
|
|
@@ -781,20 +781,20 @@ Semantic conventions for AMQP messaging following OpenTelemetry standards.
|
|
|
781
781
|
|
|
782
782
|
| Name | Type | Default value | Defined in |
|
|
783
783
|
| ------ | ------ | ------ | ------ |
|
|
784
|
-
| <a id="property-amqp_consumer_name"></a> `AMQP_CONSUMER_NAME` | `"amqp.consumer.name"` | `"amqp.consumer.name"` | [packages/core/src/telemetry.ts:37](https://github.com/btravstack/amqp-contract/blob/
|
|
785
|
-
| <a id="property-amqp_publisher_name"></a> `AMQP_PUBLISHER_NAME` | `"amqp.publisher.name"` | `"amqp.publisher.name"` | [packages/core/src/telemetry.ts:36](https://github.com/btravstack/amqp-contract/blob/
|
|
786
|
-
| <a id="property-error_type"></a> `ERROR_TYPE` | `"error.type"` | `"error.type"` | [packages/core/src/telemetry.ts:40](https://github.com/btravstack/amqp-contract/blob/
|
|
787
|
-
| <a id="property-messaging_destination"></a> `MESSAGING_DESTINATION` | `"messaging.destination.name"` | `"messaging.destination.name"` | [packages/core/src/telemetry.ts:29](https://github.com/btravstack/amqp-contract/blob/
|
|
788
|
-
| <a id="property-messaging_destination_kind"></a> `MESSAGING_DESTINATION_KIND` | `"messaging.destination.kind"` | `"messaging.destination.kind"` | [packages/core/src/telemetry.ts:30](https://github.com/btravstack/amqp-contract/blob/
|
|
789
|
-
| <a id="property-messaging_destination_kind_exchange"></a> `MESSAGING_DESTINATION_KIND_EXCHANGE` | `"exchange"` | `"exchange"` | [packages/core/src/telemetry.ts:44](https://github.com/btravstack/amqp-contract/blob/
|
|
790
|
-
| <a id="property-messaging_destination_kind_queue"></a> `MESSAGING_DESTINATION_KIND_QUEUE` | `"queue"` | `"queue"` | [packages/core/src/telemetry.ts:45](https://github.com/btravstack/amqp-contract/blob/
|
|
791
|
-
| <a id="property-messaging_operation"></a> `MESSAGING_OPERATION` | `"messaging.operation"` | `"messaging.operation"` | [packages/core/src/telemetry.ts:31](https://github.com/btravstack/amqp-contract/blob/
|
|
792
|
-
| <a id="property-messaging_operation_process"></a> `MESSAGING_OPERATION_PROCESS` | `"process"` | `"process"` | [packages/core/src/telemetry.ts:47](https://github.com/btravstack/amqp-contract/blob/
|
|
793
|
-
| <a id="property-messaging_operation_publish"></a> `MESSAGING_OPERATION_PUBLISH` | `"publish"` | `"publish"` | [packages/core/src/telemetry.ts:46](https://github.com/btravstack/amqp-contract/blob/
|
|
794
|
-
| <a id="property-messaging_rabbitmq_message_delivery_tag"></a> `MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG` | `"messaging.rabbitmq.message.delivery_tag"` | `"messaging.rabbitmq.message.delivery_tag"` | [packages/core/src/telemetry.ts:35](https://github.com/btravstack/amqp-contract/blob/
|
|
795
|
-
| <a id="property-messaging_rabbitmq_routing_key"></a> `MESSAGING_RABBITMQ_ROUTING_KEY` | `"messaging.rabbitmq.destination.routing_key"` | `"messaging.rabbitmq.destination.routing_key"` | [packages/core/src/telemetry.ts:34](https://github.com/btravstack/amqp-contract/blob/
|
|
796
|
-
| <a id="property-messaging_system"></a> `MESSAGING_SYSTEM` | `"messaging.system"` | `"messaging.system"` | [packages/core/src/telemetry.ts:28](https://github.com/btravstack/amqp-contract/blob/
|
|
797
|
-
| <a id="property-messaging_system_rabbitmq"></a> `MESSAGING_SYSTEM_RABBITMQ` | `"rabbitmq"` | `"rabbitmq"` | [packages/core/src/telemetry.ts:43](https://github.com/btravstack/amqp-contract/blob/
|
|
784
|
+
| <a id="property-amqp_consumer_name"></a> `AMQP_CONSUMER_NAME` | `"amqp.consumer.name"` | `"amqp.consumer.name"` | [packages/core/src/telemetry.ts:37](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L37) |
|
|
785
|
+
| <a id="property-amqp_publisher_name"></a> `AMQP_PUBLISHER_NAME` | `"amqp.publisher.name"` | `"amqp.publisher.name"` | [packages/core/src/telemetry.ts:36](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L36) |
|
|
786
|
+
| <a id="property-error_type"></a> `ERROR_TYPE` | `"error.type"` | `"error.type"` | [packages/core/src/telemetry.ts:40](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L40) |
|
|
787
|
+
| <a id="property-messaging_destination"></a> `MESSAGING_DESTINATION` | `"messaging.destination.name"` | `"messaging.destination.name"` | [packages/core/src/telemetry.ts:29](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L29) |
|
|
788
|
+
| <a id="property-messaging_destination_kind"></a> `MESSAGING_DESTINATION_KIND` | `"messaging.destination.kind"` | `"messaging.destination.kind"` | [packages/core/src/telemetry.ts:30](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L30) |
|
|
789
|
+
| <a id="property-messaging_destination_kind_exchange"></a> `MESSAGING_DESTINATION_KIND_EXCHANGE` | `"exchange"` | `"exchange"` | [packages/core/src/telemetry.ts:44](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L44) |
|
|
790
|
+
| <a id="property-messaging_destination_kind_queue"></a> `MESSAGING_DESTINATION_KIND_QUEUE` | `"queue"` | `"queue"` | [packages/core/src/telemetry.ts:45](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L45) |
|
|
791
|
+
| <a id="property-messaging_operation"></a> `MESSAGING_OPERATION` | `"messaging.operation"` | `"messaging.operation"` | [packages/core/src/telemetry.ts:31](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L31) |
|
|
792
|
+
| <a id="property-messaging_operation_process"></a> `MESSAGING_OPERATION_PROCESS` | `"process"` | `"process"` | [packages/core/src/telemetry.ts:47](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L47) |
|
|
793
|
+
| <a id="property-messaging_operation_publish"></a> `MESSAGING_OPERATION_PUBLISH` | `"publish"` | `"publish"` | [packages/core/src/telemetry.ts:46](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L46) |
|
|
794
|
+
| <a id="property-messaging_rabbitmq_message_delivery_tag"></a> `MESSAGING_RABBITMQ_MESSAGE_DELIVERY_TAG` | `"messaging.rabbitmq.message.delivery_tag"` | `"messaging.rabbitmq.message.delivery_tag"` | [packages/core/src/telemetry.ts:35](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L35) |
|
|
795
|
+
| <a id="property-messaging_rabbitmq_routing_key"></a> `MESSAGING_RABBITMQ_ROUTING_KEY` | `"messaging.rabbitmq.destination.routing_key"` | `"messaging.rabbitmq.destination.routing_key"` | [packages/core/src/telemetry.ts:34](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L34) |
|
|
796
|
+
| <a id="property-messaging_system"></a> `MESSAGING_SYSTEM` | `"messaging.system"` | `"messaging.system"` | [packages/core/src/telemetry.ts:28](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L28) |
|
|
797
|
+
| <a id="property-messaging_system_rabbitmq"></a> `MESSAGING_SYSTEM_RABBITMQ` | `"rabbitmq"` | `"rabbitmq"` | [packages/core/src/telemetry.ts:43](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L43) |
|
|
798
798
|
|
|
799
799
|
#### See
|
|
800
800
|
|
|
@@ -808,7 +808,7 @@ https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/
|
|
|
808
808
|
function endSpanError(span, error): void;
|
|
809
809
|
```
|
|
810
810
|
|
|
811
|
-
Defined in: [packages/core/src/telemetry.ts:324](https://github.com/btravstack/amqp-contract/blob/
|
|
811
|
+
Defined in: [packages/core/src/telemetry.ts:324](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L324)
|
|
812
812
|
|
|
813
813
|
End a span with error status.
|
|
814
814
|
|
|
@@ -831,7 +831,7 @@ End a span with error status.
|
|
|
831
831
|
function endSpanSuccess(span): void;
|
|
832
832
|
```
|
|
833
833
|
|
|
834
|
-
Defined in: [packages/core/src/telemetry.ts:309](https://github.com/btravstack/amqp-contract/blob/
|
|
834
|
+
Defined in: [packages/core/src/telemetry.ts:309](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L309)
|
|
835
835
|
|
|
836
836
|
End a span with success status.
|
|
837
837
|
|
|
@@ -858,7 +858,7 @@ function recordConsumeMetric(
|
|
|
858
858
|
durationMs): void;
|
|
859
859
|
```
|
|
860
860
|
|
|
861
|
-
Defined in: [packages/core/src/telemetry.ts:368](https://github.com/btravstack/amqp-contract/blob/
|
|
861
|
+
Defined in: [packages/core/src/telemetry.ts:368](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L368)
|
|
862
862
|
|
|
863
863
|
Record a consume metric.
|
|
864
864
|
|
|
@@ -884,7 +884,7 @@ Record a consume metric.
|
|
|
884
884
|
function recordLateRpcReply(provider, reason): void;
|
|
885
885
|
```
|
|
886
886
|
|
|
887
|
-
Defined in: [packages/core/src/telemetry.ts:398](https://github.com/btravstack/amqp-contract/blob/
|
|
887
|
+
Defined in: [packages/core/src/telemetry.ts:398](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L398)
|
|
888
888
|
|
|
889
889
|
Record an RPC reply that arrived after the caller stopped waiting.
|
|
890
890
|
|
|
@@ -912,7 +912,7 @@ function recordPublishMetric(
|
|
|
912
912
|
durationMs): void;
|
|
913
913
|
```
|
|
914
914
|
|
|
915
|
-
Defined in: [packages/core/src/telemetry.ts:341](https://github.com/btravstack/amqp-contract/blob/
|
|
915
|
+
Defined in: [packages/core/src/telemetry.ts:341](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L341)
|
|
916
916
|
|
|
917
917
|
Record a publish metric.
|
|
918
918
|
|
|
@@ -938,7 +938,7 @@ Record a publish metric.
|
|
|
938
938
|
function safeJsonParse<E>(buffer, errorFn): Result<unknown, E>;
|
|
939
939
|
```
|
|
940
940
|
|
|
941
|
-
Defined in: [packages/core/src/parsing.ts:24](https://github.com/btravstack/amqp-contract/blob/
|
|
941
|
+
Defined in: [packages/core/src/parsing.ts:24](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/parsing.ts#L24)
|
|
942
942
|
|
|
943
943
|
Parse a `Buffer` as JSON, mapping any `JSON.parse` exception to the
|
|
944
944
|
caller-supplied error type.
|
|
@@ -983,7 +983,7 @@ const parsed = safeJsonParse(
|
|
|
983
983
|
function setupAmqpTopology(channel, contract): Promise<void>;
|
|
984
984
|
```
|
|
985
985
|
|
|
986
|
-
Defined in: [packages/core/src/setup.ts:26](https://github.com/btravstack/amqp-contract/blob/
|
|
986
|
+
Defined in: [packages/core/src/setup.ts:26](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/setup.ts#L26)
|
|
987
987
|
|
|
988
988
|
Setup AMQP topology (exchanges, queues, and bindings) from a contract definition.
|
|
989
989
|
|
|
@@ -1031,7 +1031,7 @@ function startConsumeSpan(
|
|
|
1031
1031
|
attributes?): Span | undefined;
|
|
1032
1032
|
```
|
|
1033
1033
|
|
|
1034
|
-
Defined in: [packages/core/src/telemetry.ts:277](https://github.com/btravstack/amqp-contract/blob/
|
|
1034
|
+
Defined in: [packages/core/src/telemetry.ts:277](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L277)
|
|
1035
1035
|
|
|
1036
1036
|
Create a span for a consume/process operation.
|
|
1037
1037
|
Returns undefined if OpenTelemetry is not available.
|
|
@@ -1061,7 +1061,7 @@ function startPublishSpan(
|
|
|
1061
1061
|
attributes?): Span | undefined;
|
|
1062
1062
|
```
|
|
1063
1063
|
|
|
1064
|
-
Defined in: [packages/core/src/telemetry.ts:242](https://github.com/btravstack/amqp-contract/blob/
|
|
1064
|
+
Defined in: [packages/core/src/telemetry.ts:242](https://github.com/btravstack/amqp-contract/blob/cbafe3c4c44a5efd57ab3b8ebb53564b509d9d31/packages/core/src/telemetry.ts#L242)
|
|
1065
1065
|
|
|
1066
1066
|
Create a span for a publish operation.
|
|
1067
1067
|
Returns undefined if OpenTelemetry is not available.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amqp-contract/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Core utilities for AMQP setup and management in amqp-contract",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amqp",
|
|
@@ -52,20 +52,20 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"amqp-connection-manager": "5.0.0",
|
|
54
54
|
"amqplib": "2.0.1",
|
|
55
|
-
"unthrown": "0.
|
|
56
|
-
"@amqp-contract/contract": "
|
|
55
|
+
"unthrown": "1.0.0",
|
|
56
|
+
"@amqp-contract/contract": "2.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@opentelemetry/api": "1.9.1",
|
|
60
60
|
"@types/node": "24.13.2",
|
|
61
|
-
"@unthrown/vitest": "0.
|
|
61
|
+
"@unthrown/vitest": "1.0.0",
|
|
62
62
|
"@vitest/coverage-v8": "4.1.9",
|
|
63
63
|
"tsdown": "0.22.3",
|
|
64
64
|
"typedoc": "0.28.19",
|
|
65
65
|
"typescript": "6.0.3",
|
|
66
66
|
"vitest": "4.1.9",
|
|
67
67
|
"zod": "4.4.3",
|
|
68
|
-
"@amqp-contract/testing": "
|
|
68
|
+
"@amqp-contract/testing": "2.0.0",
|
|
69
69
|
"@amqp-contract/tsconfig": "0.1.0",
|
|
70
70
|
"@amqp-contract/typedoc": "0.1.0"
|
|
71
71
|
},
|