@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/README.md +2 -2
- package/dist/index.cjs +105 -41
- package/dist/index.d.cts +105 -62
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +103 -60
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +98 -29
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +361 -81
- package/package.json +25 -25
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
|
-
*
|
|
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?:
|
|
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?:
|
|
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
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/
|
|
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
|
|
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
|
-
})))).
|
|
201
|
-
|
|
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
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
}))).
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
}))).
|
|
237
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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)).
|
|
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
|