@amqp-contract/core 0.20.0 → 0.22.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.d.mts CHANGED
@@ -4,62 +4,6 @@ import { ContractDefinition } from "@amqp-contract/contract";
4
4
  import { Attributes, Counter, Histogram, Span, Tracer } from "@opentelemetry/api";
5
5
  import { Channel, ConsumeMessage, Options } from "amqplib";
6
6
 
7
- //#region src/logger.d.ts
8
- /**
9
- * Context object for logger methods.
10
- *
11
- * This type includes reserved keys that provide consistent naming
12
- * for common logging context properties.
13
- *
14
- * @property error - Error object or error details
15
- */
16
- type LoggerContext = Record<string, unknown> & {
17
- error?: unknown;
18
- };
19
- /**
20
- * Logger interface for amqp-contract packages.
21
- *
22
- * Provides a simple logging abstraction that can be implemented by users
23
- * to integrate with their preferred logging framework.
24
- *
25
- * @example
26
- * ```typescript
27
- * // Simple console logger implementation
28
- * const logger: Logger = {
29
- * debug: (message, context) => console.debug(message, context),
30
- * info: (message, context) => console.info(message, context),
31
- * warn: (message, context) => console.warn(message, context),
32
- * error: (message, context) => console.error(message, context),
33
- * };
34
- * ```
35
- */
36
- type Logger = {
37
- /**
38
- * Log debug level messages
39
- * @param message - The log message
40
- * @param context - Optional context to include with the log
41
- */
42
- debug(message: string, context?: LoggerContext): void;
43
- /**
44
- * Log info level messages
45
- * @param message - The log message
46
- * @param context - Optional context to include with the log
47
- */
48
- info(message: string, context?: LoggerContext): void;
49
- /**
50
- * Log warning level messages
51
- * @param message - The log message
52
- * @param context - Optional context to include with the log
53
- */
54
- warn(message: string, context?: LoggerContext): void;
55
- /**
56
- * Log error level messages
57
- * @param message - The log message
58
- * @param context - Optional context to include with the log
59
- */
60
- error(message: string, context?: LoggerContext): void;
61
- };
62
- //#endregion
63
7
  //#region src/errors.d.ts
64
8
  /**
65
9
  * Error for technical/runtime failures that cannot be prevented by TypeScript.
@@ -93,16 +37,31 @@ declare class MessageValidationError extends Error {
93
37
  * @property urls - AMQP broker URL(s). Multiple URLs provide failover support.
94
38
  * @property connectionOptions - Optional connection configuration (heartbeat, reconnect settings, etc.).
95
39
  * @property channelOptions - Optional channel configuration options.
40
+ * @property connectTimeoutMs - Maximum time in ms to wait for the channel to become ready
41
+ * in `waitForConnect`. If unset, waits forever (amqp-connection-manager retries indefinitely).
96
42
  */
97
43
  type AmqpClientOptions = {
98
44
  urls: ConnectionUrl[];
99
45
  connectionOptions?: AmqpConnectionManagerOptions | undefined;
100
46
  channelOptions?: Partial<CreateChannelOpts> | undefined;
47
+ connectTimeoutMs?: number | undefined;
101
48
  };
102
49
  /**
103
50
  * Callback type for consuming messages.
104
51
  */
105
52
  type ConsumeCallback = (msg: ConsumeMessage | null) => void | Promise<void>;
53
+ /**
54
+ * Publish options that extend amqplib's Options.Publish with optional timeout support.
55
+ */
56
+ type PublishOptions = Options.Publish & {
57
+ /** Message will be rejected after timeout ms */timeout?: number;
58
+ };
59
+ /**
60
+ * Consume options that extend amqplib's Options.Consume with optional prefetch support.
61
+ */
62
+ type ConsumerOptions = Options.Consume & {
63
+ /** Number of messages to prefetch */prefetch?: number;
64
+ };
106
65
  /**
107
66
  * AMQP client that manages connections and channels with automatic topology setup.
108
67
  *
@@ -137,6 +96,7 @@ declare class AmqpClient {
137
96
  private readonly channelWrapper;
138
97
  private readonly urls;
139
98
  private readonly connectionOptions?;
99
+ private readonly connectTimeoutMs?;
140
100
  /**
141
101
  * Create a new AMQP client instance.
142
102
  *
@@ -162,7 +122,19 @@ declare class AmqpClient {
162
122
  /**
163
123
  * Wait for the channel to be connected and ready.
164
124
  *
165
- * @returns A Future that resolves when the channel is connected
125
+ * If `connectTimeoutMs` was provided in the constructor options, the returned
126
+ * Future resolves to `Result.Error<TechnicalError>` once the timeout elapses.
127
+ * Without a timeout, this waits forever — amqp-connection-manager retries
128
+ * connections indefinitely and never errors on its own.
129
+ *
130
+ * NOTE: When using `AmqpClient` directly (not via `TypedAmqpClient` /
131
+ * `TypedAmqpWorker`), the constructor has already incremented the pooled
132
+ * connection's reference count. Callers must invoke `close()` on the error
133
+ * path to release the connection — `waitForConnect` does not do this
134
+ * automatically. The typed factories handle this cleanup for you.
135
+ *
136
+ * @returns A Future resolving to `Result.Ok(void)` on connect, or
137
+ * `Result.Error(TechnicalError)` on timeout / connection failure.
166
138
  */
167
139
  waitForConnect(): Future<Result<void, TechnicalError>>;
168
140
  /**
@@ -174,7 +146,16 @@ declare class AmqpClient {
174
146
  * @param options - Optional publish options
175
147
  * @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
176
148
  */
177
- publish(exchange: string, routingKey: string, content: Buffer | unknown, options?: Options.Publish): Future<Result<boolean, TechnicalError>>;
149
+ publish(exchange: string, routingKey: string, content: Buffer | unknown, options?: PublishOptions): Future<Result<boolean, TechnicalError>>;
150
+ /**
151
+ * Publish a message directly to a queue.
152
+ *
153
+ * @param queue - The queue name
154
+ * @param content - The message content (will be JSON serialized if json: true)
155
+ * @param options - Optional publish options
156
+ * @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
157
+ */
158
+ sendToQueue(queue: string, content: Buffer | unknown, options?: PublishOptions): Future<Result<boolean, TechnicalError>>;
178
159
  /**
179
160
  * Start consuming messages from a queue.
180
161
  *
@@ -183,7 +164,7 @@ declare class AmqpClient {
183
164
  * @param options - Optional consume options
184
165
  * @returns A Future with `Result<string>` - the consumer tag
185
166
  */
186
- consume(queue: string, callback: ConsumeCallback, options?: Options.Consume): Future<Result<string, TechnicalError>>;
167
+ consume(queue: string, callback: ConsumeCallback, options?: ConsumerOptions): Future<Result<string, TechnicalError>>;
187
168
  /**
188
169
  * Cancel a consumer by its consumer tag.
189
170
  *
@@ -319,6 +300,12 @@ declare class ConnectionManagerSingleton {
319
300
  * @returns The value with all object keys sorted alphabetically
320
301
  */
321
302
  private deepSort;
303
+ /**
304
+ * Get the number of active pooled connections.
305
+ *
306
+ * @internal
307
+ */
308
+ _getConnectionCountForTesting(): number;
322
309
  /**
323
310
  * Reset all cached connections (for testing purposes)
324
311
  * @internal
@@ -326,6 +313,62 @@ declare class ConnectionManagerSingleton {
326
313
  _resetForTesting(): Promise<void>;
327
314
  }
328
315
  //#endregion
316
+ //#region src/logger.d.ts
317
+ /**
318
+ * Context object for logger methods.
319
+ *
320
+ * This type includes reserved keys that provide consistent naming
321
+ * for common logging context properties.
322
+ *
323
+ * @property error - Error object or error details
324
+ */
325
+ type LoggerContext = Record<string, unknown> & {
326
+ error?: unknown;
327
+ };
328
+ /**
329
+ * Logger interface for amqp-contract packages.
330
+ *
331
+ * Provides a simple logging abstraction that can be implemented by users
332
+ * to integrate with their preferred logging framework.
333
+ *
334
+ * @example
335
+ * ```typescript
336
+ * // Simple console logger implementation
337
+ * const logger: Logger = {
338
+ * debug: (message, context) => console.debug(message, context),
339
+ * info: (message, context) => console.info(message, context),
340
+ * warn: (message, context) => console.warn(message, context),
341
+ * error: (message, context) => console.error(message, context),
342
+ * };
343
+ * ```
344
+ */
345
+ type Logger = {
346
+ /**
347
+ * Log debug level messages
348
+ * @param message - The log message
349
+ * @param context - Optional context to include with the log
350
+ */
351
+ debug(message: string, context?: LoggerContext): void;
352
+ /**
353
+ * Log info level messages
354
+ * @param message - The log message
355
+ * @param context - Optional context to include with the log
356
+ */
357
+ info(message: string, context?: LoggerContext): void;
358
+ /**
359
+ * Log warning level messages
360
+ * @param message - The log message
361
+ * @param context - Optional context to include with the log
362
+ */
363
+ warn(message: string, context?: LoggerContext): void;
364
+ /**
365
+ * Log error level messages
366
+ * @param message - The log message
367
+ * @param context - Optional context to include with the log
368
+ */
369
+ error(message: string, context?: LoggerContext): void;
370
+ };
371
+ //#endregion
329
372
  //#region src/setup.d.ts
330
373
  /**
331
374
  * Setup AMQP topology (exchanges, queues, and bindings) from a contract definition.
@@ -438,5 +481,5 @@ declare function recordConsumeMetric(provider: TelemetryProvider, queueName: str
438
481
  */
439
482
  declare function _resetTelemetryCacheForTesting(): void;
440
483
  //#endregion
441
- export { AmqpClient, type AmqpClientOptions, ConnectionManagerSingleton, type ConsumeCallback, type Logger, type LoggerContext, MessageValidationError, MessagingSemanticConventions, TechnicalError, type TelemetryProvider, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
484
+ export { AmqpClient, type AmqpClientOptions, ConnectionManagerSingleton, type ConsumeCallback, type ConsumerOptions, type Logger, type LoggerContext, MessageValidationError, MessagingSemanticConventions, type PublishOptions, TechnicalError, type TelemetryProvider, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
442
485
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/logger.ts","../src/errors.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/setup.ts","../src/telemetry.ts"],"mappings":";;;;;;;;;;;;;;AAQA;KAAY,aAAA,GAAgB,MAAA;EAC1B,KAAA;AAAA;;AAoBF;;;;;;;;;;;;;;;;KAAY,MAAA;EAoBV;;;;;EAdA,KAAA,CAAM,OAAA,UAAiB,OAAA,GAAU,aAAA;EAqBA;;;;;EAdjC,IAAA,CAAK,OAAA,UAAiB,OAAA,GAAU,aAAA;;ACpClC;;;;ED2CE,IAAA,CAAK,OAAA,UAAiB,OAAA,GAAU,aAAA;ECxCL;;;;;ED+C3B,KAAA,CAAM,OAAA,UAAiB,OAAA,GAAU,aAAA;AAAA;;;;;;;;;cClDtB,cAAA,SAAuB,KAAA;EAAA,SAGP,KAAA;cADzB,OAAA,UACyB,KAAA;AAAA;;;ADoB7B;;;;;;;cCGa,sBAAA,SAA+B,KAAA;EAAA,SAExB,MAAA;EAAA,SACA,MAAA;cADA,MAAA,UACA,MAAA;AAAA;;;;;AD3BpB;;;;;KEoCY,iBAAA;EACV,IAAA,EAAM,aAAA;EACN,iBAAA,GAAoB,4BAAA;EACpB,cAAA,GAAiB,OAAA,CAAQ,iBAAA;AAAA;;;;KAMf,eAAA,IAAmB,GAAA,EAAK,cAAA,mBAAiC,OAAA;;;;;;;;;;;;;;;;;;;;;;AD/CrE;;;;;;;cC6Ea,UAAA;EAAA,iBAkBQ,QAAA;EAAA,iBAjBF,UAAA;EAAA,iBACA,cAAA;EAAA,iBACA,IAAA;EAAA,iBACA,iBAAA;;;;;;;;;;;;cAcE,QAAA,EAAU,kBAAA,EAC3B,OAAA,EAAS,iBAAA;;AA1Db;;;;;;;;EAuGE,aAAA,CAAA,GAAiB,qBAAA;EAtGjB;;;;;EA+GA,cAAA,CAAA,GAAkB,MAAA,CAAO,MAAA,OAAa,cAAA;EA7Gb;;;AAM3B;;;;;;EAsHE,OAAA,CACE,QAAA,UACA,UAAA,UACA,OAAA,EAAS,MAAA,YACT,OAAA,GAAU,OAAA,CAAQ,OAAA,GACjB,MAAA,CAAO,MAAA,UAAgB,cAAA;EA3HgD;;AA8B5E;;;;;;EA2GE,OAAA,CACE,KAAA,UACA,QAAA,EAAU,eAAA,EACV,OAAA,GAAU,OAAA,CAAQ,OAAA,GACjB,MAAA,CAAO,MAAA,SAAe,cAAA;EAtCA;;;;;;EAkDzB,MAAA,CAAO,WAAA,WAAsB,MAAA,CAAO,MAAA,OAAa,cAAA;EAdrC;;;;;;EA0BZ,GAAA,CAAI,GAAA,EAAK,cAAA,EAAgB,OAAA;EAZI;;;;;;;EAuB7B,IAAA,CAAK,GAAA,EAAK,cAAA,EAAgB,OAAA,YAAiB,OAAA;EA0DK;;;;;;;EA/ChD,QAAA,CAAS,KAAA,GAAQ,OAAA,EAAS,OAAA,YAAmB,OAAA;;;;;;;;;;;;EAe7C,EAAA,CAAG,KAAA,UAAe,QAAA,MAAc,IAAA;EAnF9B;;;;;;;;;;EAiGF,KAAA,CAAA,GAAS,MAAA,CAAO,MAAA,OAAa,cAAA;EA9E3B;;;;EAAA,OAgGW,+BAAA,CAAA,GAAmC,OAAA;AAAA;;;;;;;;;AFvRlD;;;;;AAqBA;;;;;cGPa,0BAAA;EAAA,eACI,QAAA;EAAA,QACP,WAAA;EAAA,QACA,SAAA;EAAA,QAED,WAAA,CAAA;EHQD;;;;;EAAA,OGDC,WAAA,CAAA,GAAe,0BAAA;EHQA;;;;;;;;;;EGStB,aAAA,CACE,IAAA,EAAM,aAAA,IACN,iBAAA,GAAoB,4BAAA,GACnB,qBAAA;;;;AFhDL;;;;;;;EE0EQ,iBAAA,CACJ,IAAA,EAAM,aAAA,IACN,iBAAA,GAAoB,4BAAA,GACnB,OAAA;EF1EwB;;;AAuB7B;;;;;;;EAvB6B,QEsGnB,mBAAA;EF7EU;;;;;;EAAA,QE+FV,gBAAA;EDrFE;;;;;;EAAA,QCiGF,QAAA;ED9FgB;;;;ECsHlB,gBAAA,CAAA,GAAoB,OAAA;AAAA;;;;;;;;AH7J5B;;;;;AAqBA;;;;;;;;;;iBIJsB,iBAAA,CACpB,OAAA,EAAS,OAAA,EACT,QAAA,EAAU,kBAAA,GACT,OAAA;;;;;;;cCJU,4BAAA;EAAA;;;;;;;;;;;;;;;;;;;KA4BD,iBAAA;ELVL;;;;EKeL,SAAA,QAAiB,MAAA;ELRe;;;;EKchC,iBAAA,QAAyB,OAAA;ELPF;;;;EKavB,iBAAA,QAAyB,OAAA;;AJ/D3B;;;EIqEE,0BAAA,QAAkC,SAAA;EJrEA;;;;EI2ElC,0BAAA,QAAkC,SAAA;AAAA;;AJjDpC;;cIiKa,wBAAA,EAA0B,iBAAA;;;;;iBAYvB,gBAAA,CACd,QAAA,EAAU,iBAAA,EACV,YAAA,UACA,UAAA,sBACA,UAAA,GAAa,UAAA,GACZ,IAAA;;;;;iBA8Ba,gBAAA,CACd,QAAA,EAAU,iBAAA,EACV,SAAA,UACA,YAAA,UACA,UAAA,GAAa,UAAA,GACZ,IAAA;;;AHzMH;iBGoOgB,cAAA,CAAe,IAAA,EAAM,IAAA;;;;iBAerB,YAAA,CAAa,IAAA,EAAM,IAAA,cAAkB,KAAA,EAAO,KAAA;;;;iBAiB5C,mBAAA,CACd,QAAA,EAAU,iBAAA,EACV,YAAA,UACA,UAAA,sBACA,OAAA,WACA,UAAA;;;;iBAsBc,mBAAA,CACd,QAAA,EAAU,iBAAA,EACV,SAAA,UACA,YAAA,UACA,OAAA,WACA,UAAA;;;;;AH3RF;iBGiTgB,8BAAA,CAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/logger.ts","../src/setup.ts","../src/telemetry.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,11 +1,9 @@
1
1
  import { createRequire } from "node:module";
2
- import { Future } from "@swan-io/boxed";
2
+ import { Future, Result } from "@swan-io/boxed";
3
3
  import amqp from "amqp-connection-manager";
4
4
  import { extractQueue } from "@amqp-contract/contract";
5
-
6
5
  //#region \0rolldown/runtime.js
7
6
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
8
-
9
7
  //#endregion
10
8
  //#region src/connection-manager.ts
11
9
  /**
@@ -121,6 +119,14 @@ var ConnectionManagerSingleton = class ConnectionManagerSingleton {
121
119
  return value;
122
120
  }
123
121
  /**
122
+ * Get the number of active pooled connections.
123
+ *
124
+ * @internal
125
+ */
126
+ _getConnectionCountForTesting() {
127
+ return this.connections.size;
128
+ }
129
+ /**
124
130
  * Reset all cached connections (for testing purposes)
125
131
  * @internal
126
132
  */
@@ -131,7 +137,6 @@ var ConnectionManagerSingleton = class ConnectionManagerSingleton {
131
137
  this.refCounts.clear();
132
138
  }
133
139
  };
134
-
135
140
  //#endregion
136
141
  //#region src/errors.ts
137
142
  /**
@@ -168,7 +173,6 @@ var MessageValidationError = class extends Error {
168
173
  if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
169
174
  }
170
175
  };
171
-
172
176
  //#endregion
173
177
  //#region src/setup.ts
174
178
  /**
@@ -192,13 +196,20 @@ var MessageValidationError = class extends Error {
192
196
  * ```
193
197
  */
194
198
  async function setupAmqpTopology(channel, contract) {
195
- const exchangeErrors = (await Promise.allSettled(Object.values(contract.exchanges ?? {}).map((exchange) => channel.assertExchange(exchange.name, exchange.type, {
199
+ const exchanges = Object.values(contract.exchanges ?? {}).filter((e) => e.name !== "");
200
+ const exchangeErrors = (await Promise.allSettled(exchanges.map((exchange) => channel.assertExchange(exchange.name, exchange.type, {
196
201
  durable: exchange.durable,
197
202
  autoDelete: exchange.autoDelete,
198
203
  internal: exchange.internal,
199
204
  arguments: exchange.arguments
200
- })))).filter((result) => result.status === "rejected");
201
- if (exchangeErrors.length > 0) throw new AggregateError(exchangeErrors.map(({ reason }) => reason), "Failed to setup exchanges");
205
+ })))).map((result, i) => ({
206
+ result,
207
+ name: exchanges[i].name
208
+ })).filter((entry) => entry.result.status === "rejected");
209
+ if (exchangeErrors.length > 0) {
210
+ const names = exchangeErrors.map((e) => e.name).join(", ");
211
+ throw new AggregateError(exchangeErrors.map(({ result }) => result.reason), `Failed to setup exchanges: ${names}`);
212
+ }
202
213
  for (const queueEntry of Object.values(contract.queues ?? {})) {
203
214
  const queue = extractQueue(queueEntry);
204
215
  if (queue.deadLetter) {
@@ -206,7 +217,8 @@ async function setupAmqpTopology(channel, contract) {
206
217
  if (!Object.values(contract.exchanges ?? {}).some((exchange) => exchange.name === dlxName)) throw new TechnicalError(`Queue "${queue.name}" references dead letter exchange "${dlxName}" which is not declared in the contract. Add the exchange to contract.exchanges to ensure it is created before the queue.`);
207
218
  }
208
219
  }
209
- const queueErrors = (await Promise.allSettled(Object.values(contract.queues ?? {}).map((queueEntry) => {
220
+ const queueEntries = Object.values(contract.queues ?? {});
221
+ const queueErrors = (await Promise.allSettled(queueEntries.map((queueEntry) => {
210
222
  const queue = extractQueue(queueEntry);
211
223
  const queueArguments = { ...queue.arguments };
212
224
  queueArguments["x-queue-type"] = queue.type;
@@ -214,29 +226,41 @@ async function setupAmqpTopology(channel, contract) {
214
226
  queueArguments["x-dead-letter-exchange"] = queue.deadLetter.exchange.name;
215
227
  if (queue.deadLetter.routingKey) queueArguments["x-dead-letter-routing-key"] = queue.deadLetter.routingKey;
216
228
  }
217
- if (queue.type === "quorum") {
218
- if (queue.deliveryLimit !== void 0) queueArguments["x-delivery-limit"] = queue.deliveryLimit;
219
- return channel.assertQueue(queue.name, {
220
- durable: true,
221
- autoDelete: queue.autoDelete,
222
- arguments: queueArguments
223
- });
224
- }
229
+ if (queue.type === "quorum") return channel.assertQueue(queue.name, {
230
+ durable: true,
231
+ arguments: queueArguments
232
+ });
233
+ if (queue.maxPriority !== void 0) queueArguments["x-max-priority"] = queue.maxPriority;
225
234
  return channel.assertQueue(queue.name, {
226
235
  durable: queue.durable,
227
236
  exclusive: queue.exclusive,
228
237
  autoDelete: queue.autoDelete,
229
238
  arguments: queueArguments
230
239
  });
231
- }))).filter((result) => result.status === "rejected");
232
- if (queueErrors.length > 0) throw new AggregateError(queueErrors.map(({ reason }) => reason), "Failed to setup queues");
233
- const bindingErrors = (await Promise.allSettled(Object.values(contract.bindings ?? {}).map((binding) => {
240
+ }))).map((result, i) => ({
241
+ result,
242
+ name: extractQueue(queueEntries[i]).name
243
+ })).filter((entry) => entry.result.status === "rejected");
244
+ if (queueErrors.length > 0) {
245
+ const names = queueErrors.map((e) => e.name).join(", ");
246
+ throw new AggregateError(queueErrors.map(({ result }) => result.reason), `Failed to setup queues: ${names}`);
247
+ }
248
+ const bindings = Object.values(contract.bindings ?? {});
249
+ const bindingErrors = (await Promise.allSettled(bindings.map((binding) => {
234
250
  if (binding.type === "queue") return channel.bindQueue(binding.queue.name, binding.exchange.name, binding.routingKey ?? "", binding.arguments);
235
251
  return channel.bindExchange(binding.destination.name, binding.source.name, binding.routingKey ?? "", binding.arguments);
236
- }))).filter((result) => result.status === "rejected");
237
- if (bindingErrors.length > 0) throw new AggregateError(bindingErrors.map(({ reason }) => reason), "Failed to setup bindings");
252
+ }))).map((result, i) => {
253
+ const binding = bindings[i];
254
+ return {
255
+ result,
256
+ name: binding.type === "queue" ? `${binding.exchange.name} -> ${binding.queue.name}` : `${binding.source.name} -> ${binding.destination.name}`
257
+ };
258
+ }).filter((entry) => entry.result.status === "rejected");
259
+ if (bindingErrors.length > 0) {
260
+ const names = bindingErrors.map((e) => e.name).join(", ");
261
+ throw new AggregateError(bindingErrors.map(({ result }) => result.reason), `Failed to setup bindings: ${names}`);
262
+ }
238
263
  }
239
-
240
264
  //#endregion
241
265
  //#region src/amqp-client.ts
242
266
  /**
@@ -286,6 +310,7 @@ var AmqpClient = class {
286
310
  channelWrapper;
287
311
  urls;
288
312
  connectionOptions;
313
+ connectTimeoutMs;
289
314
  /**
290
315
  * Create a new AMQP client instance.
291
316
  *
@@ -301,10 +326,13 @@ var AmqpClient = class {
301
326
  this.contract = contract;
302
327
  this.urls = options.urls;
303
328
  if (options.connectionOptions !== void 0) this.connectionOptions = options.connectionOptions;
304
- this.connection = ConnectionManagerSingleton.getInstance().getConnection(options.urls, options.connectionOptions);
329
+ if (options.connectTimeoutMs !== void 0) this.connectTimeoutMs = options.connectTimeoutMs;
330
+ const singleton = ConnectionManagerSingleton.getInstance();
331
+ this.connection = singleton.getConnection(options.urls, options.connectionOptions);
305
332
  const defaultSetup = (channel) => setupAmqpTopology(channel, this.contract);
306
333
  const { setup: userSetup, ...otherChannelOptions } = options.channelOptions ?? {};
307
334
  const channelOpts = {
335
+ confirm: true,
308
336
  json: true,
309
337
  setup: defaultSetup,
310
338
  ...otherChannelOptions
@@ -330,10 +358,36 @@ var AmqpClient = class {
330
358
  /**
331
359
  * Wait for the channel to be connected and ready.
332
360
  *
333
- * @returns A Future that resolves when the channel is connected
361
+ * If `connectTimeoutMs` was provided in the constructor options, the returned
362
+ * Future resolves to `Result.Error<TechnicalError>` once the timeout elapses.
363
+ * Without a timeout, this waits forever — amqp-connection-manager retries
364
+ * connections indefinitely and never errors on its own.
365
+ *
366
+ * NOTE: When using `AmqpClient` directly (not via `TypedAmqpClient` /
367
+ * `TypedAmqpWorker`), the constructor has already incremented the pooled
368
+ * connection's reference count. Callers must invoke `close()` on the error
369
+ * path to release the connection — `waitForConnect` does not do this
370
+ * automatically. The typed factories handle this cleanup for you.
371
+ *
372
+ * @returns A Future resolving to `Result.Ok(void)` on connect, or
373
+ * `Result.Error(TechnicalError)` on timeout / connection failure.
334
374
  */
335
375
  waitForConnect() {
336
- return Future.fromPromise(this.channelWrapper.waitForConnect()).mapError((error) => new TechnicalError("Failed to connect to AMQP broker", error));
376
+ const connectPromise = this.channelWrapper.waitForConnect();
377
+ const racedPromise = this.connectTimeoutMs === void 0 ? connectPromise : new Promise((resolve, reject) => {
378
+ const timeoutMs = this.connectTimeoutMs;
379
+ const handle = setTimeout(() => {
380
+ reject(/* @__PURE__ */ new Error(`Timed out waiting for AMQP connection after ${timeoutMs}ms`));
381
+ }, timeoutMs);
382
+ connectPromise.then(() => {
383
+ clearTimeout(handle);
384
+ resolve();
385
+ }, (error) => {
386
+ clearTimeout(handle);
387
+ reject(error);
388
+ });
389
+ });
390
+ return Future.fromPromise(racedPromise).mapError((error) => new TechnicalError("Failed to connect to AMQP broker", error));
337
391
  }
338
392
  /**
339
393
  * Publish a message to an exchange.
@@ -348,6 +402,17 @@ var AmqpClient = class {
348
402
  return Future.fromPromise(this.channelWrapper.publish(exchange, routingKey, content, options)).mapError((error) => new TechnicalError("Failed to publish message", error));
349
403
  }
350
404
  /**
405
+ * Publish a message directly to a queue.
406
+ *
407
+ * @param queue - The queue name
408
+ * @param content - The message content (will be JSON serialized if json: true)
409
+ * @param options - Optional publish options
410
+ * @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
411
+ */
412
+ sendToQueue(queue, content, options) {
413
+ return Future.fromPromise(this.channelWrapper.sendToQueue(queue, content, options)).mapError((error) => new TechnicalError("Failed to publish message to queue", error));
414
+ }
415
+ /**
351
416
  * Start consuming messages from a queue.
352
417
  *
353
418
  * @param queue - The queue name
@@ -421,7 +486,10 @@ var AmqpClient = class {
421
486
  * @returns A Future that resolves when the channel and connection are closed
422
487
  */
423
488
  close() {
424
- return Future.fromPromise(this.channelWrapper.close()).mapError((error) => new TechnicalError("Failed to close channel", error)).flatMapOk(() => Future.fromPromise(ConnectionManagerSingleton.getInstance().releaseConnection(this.urls, this.connectionOptions)).mapError((error) => new TechnicalError("Failed to release connection", error))).mapOk(() => void 0);
489
+ return Future.fromPromise(this.channelWrapper.close()).mapError((error) => new TechnicalError("Failed to close channel", error)).flatMap((channelResult) => Future.fromPromise(ConnectionManagerSingleton.getInstance().releaseConnection(this.urls, this.connectionOptions)).mapError((error) => new TechnicalError("Failed to release connection", error)).map((releaseResult) => {
490
+ if (channelResult.isError() && releaseResult.isError()) return Result.Error(new TechnicalError("Failed to close channel and release connection", new AggregateError([channelResult.error, releaseResult.error], "Failed to close channel and release connection")));
491
+ return channelResult.isError() ? channelResult : releaseResult;
492
+ }));
425
493
  }
426
494
  /**
427
495
  * Reset connection singleton cache (for testing only)
@@ -431,7 +499,6 @@ var AmqpClient = class {
431
499
  await ConnectionManagerSingleton.getInstance()._resetForTesting();
432
500
  }
433
501
  };
434
-
435
502
  //#endregion
436
503
  //#region src/telemetry.ts
437
504
  /**
@@ -440,7 +507,9 @@ var AmqpClient = class {
440
507
  * @see https://opentelemetry.io/docs/specs/otel/trace/api/#spankind
441
508
  */
442
509
  const SpanKind = {
510
+ /** Producer span represents a message producer */
443
511
  PRODUCER: 3,
512
+ /** Consumer span represents a message consumer */
444
513
  CONSUMER: 4
445
514
  };
446
515
  /**
@@ -655,7 +724,7 @@ function _resetTelemetryCacheForTesting() {
655
724
  cachedPublishLatencyHistogram = void 0;
656
725
  cachedConsumeLatencyHistogram = void 0;
657
726
  }
658
-
659
727
  //#endregion
660
728
  export { AmqpClient, ConnectionManagerSingleton, MessageValidationError, MessagingSemanticConventions, TechnicalError, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
729
+
661
730
  //# sourceMappingURL=index.mjs.map