@amqp-contract/core 0.23.1 → 0.25.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 +8 -6
- package/dist/index.cjs +101 -37
- package/dist/index.d.cts +84 -41
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +84 -41
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +101 -38
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +186 -124
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/btravers/amqp-contract/actions/workflows/ci.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/@amqp-contract/core)
|
|
7
7
|
[](https://www.npmjs.com/package/@amqp-contract/core)
|
|
8
|
-
[](https://www.typescriptlang.org/)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
|
|
11
11
|
This package provides centralized functionality for establishing AMQP topology (exchanges, queues, and bindings) from contract definitions, and defines the `Logger` interface used across amqp-contract packages.
|
|
@@ -87,11 +87,13 @@ const logger: Logger = {
|
|
|
87
87
|
// Pass the logger to client or worker
|
|
88
88
|
import { TypedAmqpClient } from "@amqp-contract/client";
|
|
89
89
|
|
|
90
|
-
const client =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
const client = (
|
|
91
|
+
await TypedAmqpClient.create({
|
|
92
|
+
contract,
|
|
93
|
+
urls: ["amqp://localhost"],
|
|
94
|
+
logger, // Optional: logs published messages
|
|
95
|
+
})
|
|
96
|
+
)._unsafeUnwrap();
|
|
95
97
|
```
|
|
96
98
|
|
|
97
99
|
## API
|
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
enumerable: true
|
|
22
22
|
}) : target, mod));
|
|
23
23
|
//#endregion
|
|
24
|
-
let
|
|
24
|
+
let neverthrow = require("neverthrow");
|
|
25
25
|
let amqp_connection_manager = require("amqp-connection-manager");
|
|
26
26
|
amqp_connection_manager = __toESM(amqp_connection_manager, 1);
|
|
27
27
|
let _amqp_contract_contract = require("@amqp-contract/contract");
|
|
@@ -321,7 +321,7 @@ function callSetupFunc(setup, channel) {
|
|
|
321
321
|
* Default time `waitForConnect` will wait for the broker before erroring out.
|
|
322
322
|
* Defaulting to a finite value (rather than waiting forever) means a fail-fast
|
|
323
323
|
* developer experience: a misconfigured URL, a down broker, or wrong
|
|
324
|
-
* credentials surface as
|
|
324
|
+
* credentials surface as an `err` within 30 seconds. Pass `null`
|
|
325
325
|
* explicitly to disable the timeout — `Infinity` and other non-finite values
|
|
326
326
|
* are also coerced to "no timeout" because Node's `setTimeout` clamps large
|
|
327
327
|
* delays to ~24.8 days and silently fires near-immediately on `Infinity`.
|
|
@@ -348,7 +348,7 @@ function resolveConnectTimeoutMs(input) {
|
|
|
348
348
|
* - Automatic AMQP topology setup (exchanges, queues, bindings) from contract
|
|
349
349
|
* - Channel creation with JSON serialization enabled by default
|
|
350
350
|
*
|
|
351
|
-
* All operations return `
|
|
351
|
+
* All operations return `ResultAsync<T, TechnicalError>` for consistent error handling.
|
|
352
352
|
*
|
|
353
353
|
* @example
|
|
354
354
|
* ```typescript
|
|
@@ -357,14 +357,14 @@ function resolveConnectTimeoutMs(input) {
|
|
|
357
357
|
* connectionOptions: { heartbeatIntervalInSeconds: 30 }
|
|
358
358
|
* });
|
|
359
359
|
*
|
|
360
|
-
* // Wait for connection
|
|
361
|
-
* await client.waitForConnect()
|
|
360
|
+
* // Wait for connection (ResultAsync is thenable)
|
|
361
|
+
* await client.waitForConnect();
|
|
362
362
|
*
|
|
363
363
|
* // Publish a message
|
|
364
|
-
* const result = await client.publish('exchange', 'routingKey', { data: 'value' })
|
|
364
|
+
* const result = await client.publish('exchange', 'routingKey', { data: 'value' });
|
|
365
365
|
*
|
|
366
366
|
* // Close when done
|
|
367
|
-
* await client.close()
|
|
367
|
+
* await client.close();
|
|
368
368
|
* ```
|
|
369
369
|
*/
|
|
370
370
|
var AmqpClient = class {
|
|
@@ -375,6 +375,15 @@ var AmqpClient = class {
|
|
|
375
375
|
/** Resolved timeout in ms; `null` means "wait forever". */
|
|
376
376
|
connectTimeoutMs;
|
|
377
377
|
/**
|
|
378
|
+
* Per-consumer prefetch setup functions registered via `addSetup` so they
|
|
379
|
+
* can be removed in {@link cancel} once the consumer is gone — otherwise
|
|
380
|
+
* the channel wrapper would replay the cancelled consumer's QoS on every
|
|
381
|
+
* reconnect and silently apply it to subsequent consumers.
|
|
382
|
+
*
|
|
383
|
+
* @internal
|
|
384
|
+
*/
|
|
385
|
+
prefetchSetups = /* @__PURE__ */ new Map();
|
|
386
|
+
/**
|
|
378
387
|
* Create a new AMQP client instance.
|
|
379
388
|
*
|
|
380
389
|
* The client will automatically:
|
|
@@ -422,7 +431,7 @@ var AmqpClient = class {
|
|
|
422
431
|
* Wait for the channel to be connected and ready.
|
|
423
432
|
*
|
|
424
433
|
* If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
425
|
-
*
|
|
434
|
+
* ResultAsync resolves to `err(TechnicalError)` once the timeout elapses.
|
|
426
435
|
* Without a timeout, this waits forever — amqp-connection-manager retries
|
|
427
436
|
* connections indefinitely and never errors on its own.
|
|
428
437
|
*
|
|
@@ -431,9 +440,6 @@ var AmqpClient = class {
|
|
|
431
440
|
* connection's reference count. Callers must invoke `close()` on the error
|
|
432
441
|
* path to release the connection — `waitForConnect` does not do this
|
|
433
442
|
* automatically. The typed factories handle this cleanup for you.
|
|
434
|
-
*
|
|
435
|
-
* @returns A Future resolving to `Result.Ok(void)` on connect, or
|
|
436
|
-
* `Result.Error(TechnicalError)` on timeout / connection failure.
|
|
437
443
|
*/
|
|
438
444
|
waitForConnect() {
|
|
439
445
|
const connectPromise = this.channelWrapper.waitForConnect();
|
|
@@ -450,50 +456,76 @@ var AmqpClient = class {
|
|
|
450
456
|
reject(error);
|
|
451
457
|
});
|
|
452
458
|
});
|
|
453
|
-
return
|
|
459
|
+
return neverthrow.ResultAsync.fromPromise(racedPromise, (error) => new TechnicalError("Failed to connect to AMQP broker", error));
|
|
454
460
|
}
|
|
455
461
|
/**
|
|
456
462
|
* Publish a message to an exchange.
|
|
457
463
|
*
|
|
458
|
-
* @
|
|
459
|
-
* @param routingKey - The routing key
|
|
460
|
-
* @param content - The message content (will be JSON serialized if json: true)
|
|
461
|
-
* @param options - Optional publish options
|
|
462
|
-
* @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
|
|
464
|
+
* @returns ResultAsync resolving to `true` if the message was sent, `false` if the channel buffer is full.
|
|
463
465
|
*/
|
|
464
466
|
publish(exchange, routingKey, content, options) {
|
|
465
|
-
return
|
|
467
|
+
return neverthrow.ResultAsync.fromPromise(this.channelWrapper.publish(exchange, routingKey, content, options), (error) => new TechnicalError("Failed to publish message", error));
|
|
466
468
|
}
|
|
467
469
|
/**
|
|
468
470
|
* Publish a message directly to a queue.
|
|
469
471
|
*
|
|
470
|
-
* @
|
|
471
|
-
* @param content - The message content (will be JSON serialized if json: true)
|
|
472
|
-
* @param options - Optional publish options
|
|
473
|
-
* @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
|
|
472
|
+
* @returns ResultAsync resolving to `true` if the message was sent, `false` if the channel buffer is full.
|
|
474
473
|
*/
|
|
475
474
|
sendToQueue(queue, content, options) {
|
|
476
|
-
return
|
|
475
|
+
return neverthrow.ResultAsync.fromPromise(this.channelWrapper.sendToQueue(queue, content, options), (error) => new TechnicalError("Failed to publish message to queue", error));
|
|
477
476
|
}
|
|
478
477
|
/**
|
|
479
478
|
* Start consuming messages from a queue.
|
|
480
479
|
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
480
|
+
* If `options.prefetch` is set, a per-consumer prefetch count is applied via
|
|
481
|
+
* `channel.prefetch(count, false)` registered as a setup function on the
|
|
482
|
+
* channel wrapper *before* the underlying `consume` call. Registering it via
|
|
483
|
+
* `addSetup` ensures the prefetch is reapplied automatically on channel
|
|
484
|
+
* reconnect; using `global=false` scopes it to subsequent consumers on the
|
|
485
|
+
* channel (RabbitMQ semantics — opposite of intuition: `false` is per-
|
|
486
|
+
* consumer, `true` is channel-wide).
|
|
487
|
+
*
|
|
488
|
+
* `prefetch` is stripped from the options handed to `channelWrapper.consume`
|
|
489
|
+
* because it is not a valid `amqplib` `Options.Consume` field — leaving it
|
|
490
|
+
* in would just travel as a no-op key-value pair on the consume frame.
|
|
491
|
+
*
|
|
492
|
+
* @returns ResultAsync resolving to the consumer tag.
|
|
485
493
|
*/
|
|
486
494
|
consume(queue, callback, options) {
|
|
487
|
-
|
|
495
|
+
const { prefetch, ...consumeOptions } = options ?? {};
|
|
496
|
+
if (prefetch !== void 0) {
|
|
497
|
+
if (!Number.isInteger(prefetch) || prefetch < 0 || prefetch > 65535) return (0, neverthrow.errAsync)(new TechnicalError(`Invalid prefetch: expected a non-negative integer ≤ 65535, got ${String(prefetch)}`));
|
|
498
|
+
}
|
|
499
|
+
const prefetchSetup = typeof prefetch === "number" ? async (channel) => {
|
|
500
|
+
await channel.prefetch(prefetch, false);
|
|
501
|
+
} : void 0;
|
|
502
|
+
const consumePromise = (async () => {
|
|
503
|
+
if (prefetchSetup) await this.channelWrapper.addSetup(prefetchSetup);
|
|
504
|
+
let reply;
|
|
505
|
+
try {
|
|
506
|
+
reply = await this.channelWrapper.consume(queue, callback, consumeOptions);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (prefetchSetup) await this.channelWrapper.removeSetup(prefetchSetup).catch(() => {});
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
if (prefetchSetup) this.prefetchSetups.set(reply.consumerTag, prefetchSetup);
|
|
512
|
+
return reply;
|
|
513
|
+
})();
|
|
514
|
+
return neverthrow.ResultAsync.fromPromise(consumePromise, (error) => new TechnicalError("Failed to start consuming messages", error)).map((reply) => reply.consumerTag);
|
|
488
515
|
}
|
|
489
516
|
/**
|
|
490
517
|
* Cancel a consumer by its consumer tag.
|
|
491
|
-
*
|
|
492
|
-
* @param consumerTag - The consumer tag to cancel
|
|
493
|
-
* @returns A Future that resolves when the consumer is cancelled
|
|
494
518
|
*/
|
|
495
519
|
cancel(consumerTag) {
|
|
496
|
-
return
|
|
520
|
+
return neverthrow.ResultAsync.fromPromise((async () => {
|
|
521
|
+
const setup = this.prefetchSetups.get(consumerTag);
|
|
522
|
+
this.prefetchSetups.delete(consumerTag);
|
|
523
|
+
try {
|
|
524
|
+
await this.channelWrapper.cancel(consumerTag);
|
|
525
|
+
} finally {
|
|
526
|
+
if (setup !== void 0) await this.channelWrapper.removeSetup(setup).catch(() => {});
|
|
527
|
+
}
|
|
528
|
+
})(), (error) => new TechnicalError("Failed to cancel consumer", error)).map(() => void 0);
|
|
497
529
|
}
|
|
498
530
|
/**
|
|
499
531
|
* Acknowledge a message.
|
|
@@ -546,13 +578,18 @@ var AmqpClient = class {
|
|
|
546
578
|
* - Decrease the reference count on the shared connection
|
|
547
579
|
* - Close the connection if this was the last client using it
|
|
548
580
|
*
|
|
549
|
-
*
|
|
581
|
+
* Both steps run regardless of each other's outcome; if both fail, the
|
|
582
|
+
* errors are wrapped in an AggregateError.
|
|
550
583
|
*/
|
|
551
584
|
close() {
|
|
552
|
-
return
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
585
|
+
return new neverthrow.ResultAsync((async () => {
|
|
586
|
+
const channelResult = await neverthrow.ResultAsync.fromPromise(this.channelWrapper.close(), (error) => new TechnicalError("Failed to close channel", error));
|
|
587
|
+
const releaseResult = await neverthrow.ResultAsync.fromPromise(ConnectionManagerSingleton.getInstance().releaseConnection(this.urls, this.connectionOptions), (error) => new TechnicalError("Failed to release connection", error));
|
|
588
|
+
if (channelResult.isErr() && releaseResult.isErr()) return (0, neverthrow.err)(new TechnicalError("Failed to close channel and release connection", new AggregateError([channelResult.error, releaseResult.error], "Failed to close channel and release connection")));
|
|
589
|
+
if (channelResult.isErr()) return channelResult;
|
|
590
|
+
if (releaseResult.isErr()) return releaseResult;
|
|
591
|
+
return (0, neverthrow.ok)(void 0);
|
|
592
|
+
})());
|
|
556
593
|
}
|
|
557
594
|
/**
|
|
558
595
|
* Reset connection singleton cache (for testing only)
|
|
@@ -563,6 +600,32 @@ var AmqpClient = class {
|
|
|
563
600
|
}
|
|
564
601
|
};
|
|
565
602
|
//#endregion
|
|
603
|
+
//#region src/parsing.ts
|
|
604
|
+
/**
|
|
605
|
+
* Parse a `Buffer` as JSON, mapping any `JSON.parse` exception to the
|
|
606
|
+
* caller-supplied error type.
|
|
607
|
+
*
|
|
608
|
+
* Use this in consume / reply paths where a parse failure is a typed value,
|
|
609
|
+
* not a thrown exception — the caller decides how to translate the raw error
|
|
610
|
+
* into a domain-level error (e.g. {@link TechnicalError}).
|
|
611
|
+
*
|
|
612
|
+
* @typeParam E - The error type produced by `errorFn`.
|
|
613
|
+
* @param buffer - The raw message body to parse.
|
|
614
|
+
* @param errorFn - Callback invoked with the underlying `JSON.parse` error.
|
|
615
|
+
* @returns A `Result` containing the parsed `unknown` value or the mapped error.
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* ```typescript
|
|
619
|
+
* const parsed = safeJsonParse(
|
|
620
|
+
* msg.content,
|
|
621
|
+
* (error) => new TechnicalError("Failed to parse JSON", error),
|
|
622
|
+
* );
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
function safeJsonParse(buffer, errorFn) {
|
|
626
|
+
return neverthrow.Result.fromThrowable(() => JSON.parse(buffer.toString()), errorFn)();
|
|
627
|
+
}
|
|
628
|
+
//#endregion
|
|
566
629
|
//#region src/telemetry.ts
|
|
567
630
|
/**
|
|
568
631
|
* SpanKind values from OpenTelemetry.
|
|
@@ -841,6 +904,7 @@ exports.endSpanSuccess = endSpanSuccess;
|
|
|
841
904
|
exports.recordConsumeMetric = recordConsumeMetric;
|
|
842
905
|
exports.recordLateRpcReply = recordLateRpcReply;
|
|
843
906
|
exports.recordPublishMetric = recordPublishMetric;
|
|
907
|
+
exports.safeJsonParse = safeJsonParse;
|
|
844
908
|
exports.setupAmqpTopology = setupAmqpTopology;
|
|
845
909
|
exports.startConsumeSpan = startConsumeSpan;
|
|
846
910
|
exports.startPublishSpan = startPublishSpan;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ContractDefinition } from "@amqp-contract/contract";
|
|
2
|
-
import { Future, Result } from "@swan-io/boxed";
|
|
3
2
|
import { AmqpConnectionManager, AmqpConnectionManagerOptions, ConnectionUrl, CreateChannelOpts } from "amqp-connection-manager";
|
|
4
3
|
import { Channel, ConsumeMessage, Options } from "amqplib";
|
|
4
|
+
import { Result, ResultAsync } from "neverthrow";
|
|
5
5
|
import { Attributes, Counter, Histogram, Span, Tracer } from "@opentelemetry/api";
|
|
6
6
|
|
|
7
7
|
//#region src/errors.d.ts
|
|
@@ -35,7 +35,7 @@ declare class MessageValidationError extends Error {
|
|
|
35
35
|
* Default time `waitForConnect` will wait for the broker before erroring out.
|
|
36
36
|
* Defaulting to a finite value (rather than waiting forever) means a fail-fast
|
|
37
37
|
* developer experience: a misconfigured URL, a down broker, or wrong
|
|
38
|
-
* credentials surface as
|
|
38
|
+
* credentials surface as an `err` within 30 seconds. Pass `null`
|
|
39
39
|
* explicitly to disable the timeout — `Infinity` and other non-finite values
|
|
40
40
|
* are also coerced to "no timeout" because Node's `setTimeout` clamps large
|
|
41
41
|
* delays to ~24.8 days and silently fires near-immediately on `Infinity`.
|
|
@@ -63,16 +63,29 @@ type AmqpClientOptions = {
|
|
|
63
63
|
*/
|
|
64
64
|
type ConsumeCallback = (msg: ConsumeMessage | null) => void | Promise<void>;
|
|
65
65
|
/**
|
|
66
|
-
* Publish options
|
|
66
|
+
* Publish options for `AmqpClient.publish` / `AmqpClient.sendToQueue`.
|
|
67
|
+
*
|
|
68
|
+
* Currently a re-export of amqplib's `Options.Publish`. A previous version of
|
|
69
|
+
* this type also exposed a `timeout` field, but that field never had a
|
|
70
|
+
* meaningful AMQP-level effect in this codebase and has been removed to avoid
|
|
71
|
+
* suggesting behaviour we do not provide. (`amqp-connection-manager`'s own
|
|
72
|
+
* `publishTimeout` channel option is unrelated and is configured at channel
|
|
73
|
+
* creation, not per-publish.)
|
|
67
74
|
*/
|
|
68
|
-
type PublishOptions = Options.Publish
|
|
69
|
-
/** Message will be rejected after timeout ms */timeout?: number;
|
|
70
|
-
};
|
|
75
|
+
type PublishOptions = Options.Publish;
|
|
71
76
|
/**
|
|
72
|
-
* Consume options that extend amqplib's Options.Consume with optional
|
|
77
|
+
* Consume options that extend amqplib's `Options.Consume` with an optional
|
|
78
|
+
* per-consumer prefetch count.
|
|
79
|
+
*
|
|
80
|
+
* `prefetch` is intercepted by {@link AmqpClient.consume}: it is stripped from
|
|
81
|
+
* the options handed to the underlying `channelWrapper.consume(...)` call
|
|
82
|
+
* (since amqplib's `Options.Consume` does not include it) and applied via
|
|
83
|
+
* `channel.prefetch(count, false)` registered through `addSetup` *before* the
|
|
84
|
+
* consume so the value is in effect when the consumer starts and is reapplied
|
|
85
|
+
* automatically on channel reconnect.
|
|
73
86
|
*/
|
|
74
87
|
type ConsumerOptions = Options.Consume & {
|
|
75
|
-
/**
|
|
88
|
+
/** Per-consumer prefetch count. Applied before `channel.consume(...)`. */prefetch?: number;
|
|
76
89
|
};
|
|
77
90
|
/**
|
|
78
91
|
* AMQP client that manages connections and channels with automatic topology setup.
|
|
@@ -83,7 +96,7 @@ type ConsumerOptions = Options.Consume & {
|
|
|
83
96
|
* - Automatic AMQP topology setup (exchanges, queues, bindings) from contract
|
|
84
97
|
* - Channel creation with JSON serialization enabled by default
|
|
85
98
|
*
|
|
86
|
-
* All operations return `
|
|
99
|
+
* All operations return `ResultAsync<T, TechnicalError>` for consistent error handling.
|
|
87
100
|
*
|
|
88
101
|
* @example
|
|
89
102
|
* ```typescript
|
|
@@ -92,14 +105,14 @@ type ConsumerOptions = Options.Consume & {
|
|
|
92
105
|
* connectionOptions: { heartbeatIntervalInSeconds: 30 }
|
|
93
106
|
* });
|
|
94
107
|
*
|
|
95
|
-
* // Wait for connection
|
|
96
|
-
* await client.waitForConnect()
|
|
108
|
+
* // Wait for connection (ResultAsync is thenable)
|
|
109
|
+
* await client.waitForConnect();
|
|
97
110
|
*
|
|
98
111
|
* // Publish a message
|
|
99
|
-
* const result = await client.publish('exchange', 'routingKey', { data: 'value' })
|
|
112
|
+
* const result = await client.publish('exchange', 'routingKey', { data: 'value' });
|
|
100
113
|
*
|
|
101
114
|
* // Close when done
|
|
102
|
-
* await client.close()
|
|
115
|
+
* await client.close();
|
|
103
116
|
* ```
|
|
104
117
|
*/
|
|
105
118
|
declare class AmqpClient {
|
|
@@ -110,6 +123,15 @@ declare class AmqpClient {
|
|
|
110
123
|
private readonly connectionOptions?;
|
|
111
124
|
/** Resolved timeout in ms; `null` means "wait forever". */
|
|
112
125
|
private readonly connectTimeoutMs;
|
|
126
|
+
/**
|
|
127
|
+
* Per-consumer prefetch setup functions registered via `addSetup` so they
|
|
128
|
+
* can be removed in {@link cancel} once the consumer is gone — otherwise
|
|
129
|
+
* the channel wrapper would replay the cancelled consumer's QoS on every
|
|
130
|
+
* reconnect and silently apply it to subsequent consumers.
|
|
131
|
+
*
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
private readonly prefetchSetups;
|
|
113
135
|
/**
|
|
114
136
|
* Create a new AMQP client instance.
|
|
115
137
|
*
|
|
@@ -136,7 +158,7 @@ declare class AmqpClient {
|
|
|
136
158
|
* Wait for the channel to be connected and ready.
|
|
137
159
|
*
|
|
138
160
|
* If `connectTimeoutMs` was provided in the constructor options, the returned
|
|
139
|
-
*
|
|
161
|
+
* ResultAsync resolves to `err(TechnicalError)` once the timeout elapses.
|
|
140
162
|
* Without a timeout, this waits forever — amqp-connection-manager retries
|
|
141
163
|
* connections indefinitely and never errors on its own.
|
|
142
164
|
*
|
|
@@ -145,46 +167,42 @@ declare class AmqpClient {
|
|
|
145
167
|
* connection's reference count. Callers must invoke `close()` on the error
|
|
146
168
|
* path to release the connection — `waitForConnect` does not do this
|
|
147
169
|
* automatically. The typed factories handle this cleanup for you.
|
|
148
|
-
*
|
|
149
|
-
* @returns A Future resolving to `Result.Ok(void)` on connect, or
|
|
150
|
-
* `Result.Error(TechnicalError)` on timeout / connection failure.
|
|
151
170
|
*/
|
|
152
|
-
waitForConnect():
|
|
171
|
+
waitForConnect(): ResultAsync<void, TechnicalError>;
|
|
153
172
|
/**
|
|
154
173
|
* Publish a message to an exchange.
|
|
155
174
|
*
|
|
156
|
-
* @
|
|
157
|
-
* @param routingKey - The routing key
|
|
158
|
-
* @param content - The message content (will be JSON serialized if json: true)
|
|
159
|
-
* @param options - Optional publish options
|
|
160
|
-
* @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
|
|
175
|
+
* @returns ResultAsync resolving to `true` if the message was sent, `false` if the channel buffer is full.
|
|
161
176
|
*/
|
|
162
|
-
publish(exchange: string, routingKey: string, content: Buffer | unknown, options?: PublishOptions):
|
|
177
|
+
publish(exchange: string, routingKey: string, content: Buffer | unknown, options?: PublishOptions): ResultAsync<boolean, TechnicalError>;
|
|
163
178
|
/**
|
|
164
179
|
* Publish a message directly to a queue.
|
|
165
180
|
*
|
|
166
|
-
* @
|
|
167
|
-
* @param content - The message content (will be JSON serialized if json: true)
|
|
168
|
-
* @param options - Optional publish options
|
|
169
|
-
* @returns A Future with `Result<boolean>` - true if message was sent, false if channel buffer is full
|
|
181
|
+
* @returns ResultAsync resolving to `true` if the message was sent, `false` if the channel buffer is full.
|
|
170
182
|
*/
|
|
171
|
-
sendToQueue(queue: string, content: Buffer | unknown, options?: PublishOptions):
|
|
183
|
+
sendToQueue(queue: string, content: Buffer | unknown, options?: PublishOptions): ResultAsync<boolean, TechnicalError>;
|
|
172
184
|
/**
|
|
173
185
|
* Start consuming messages from a queue.
|
|
174
186
|
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
187
|
+
* If `options.prefetch` is set, a per-consumer prefetch count is applied via
|
|
188
|
+
* `channel.prefetch(count, false)` registered as a setup function on the
|
|
189
|
+
* channel wrapper *before* the underlying `consume` call. Registering it via
|
|
190
|
+
* `addSetup` ensures the prefetch is reapplied automatically on channel
|
|
191
|
+
* reconnect; using `global=false` scopes it to subsequent consumers on the
|
|
192
|
+
* channel (RabbitMQ semantics — opposite of intuition: `false` is per-
|
|
193
|
+
* consumer, `true` is channel-wide).
|
|
194
|
+
*
|
|
195
|
+
* `prefetch` is stripped from the options handed to `channelWrapper.consume`
|
|
196
|
+
* because it is not a valid `amqplib` `Options.Consume` field — leaving it
|
|
197
|
+
* in would just travel as a no-op key-value pair on the consume frame.
|
|
198
|
+
*
|
|
199
|
+
* @returns ResultAsync resolving to the consumer tag.
|
|
179
200
|
*/
|
|
180
|
-
consume(queue: string, callback: ConsumeCallback, options?: ConsumerOptions):
|
|
201
|
+
consume(queue: string, callback: ConsumeCallback, options?: ConsumerOptions): ResultAsync<string, TechnicalError>;
|
|
181
202
|
/**
|
|
182
203
|
* Cancel a consumer by its consumer tag.
|
|
183
|
-
*
|
|
184
|
-
* @param consumerTag - The consumer tag to cancel
|
|
185
|
-
* @returns A Future that resolves when the consumer is cancelled
|
|
186
204
|
*/
|
|
187
|
-
cancel(consumerTag: string):
|
|
205
|
+
cancel(consumerTag: string): ResultAsync<void, TechnicalError>;
|
|
188
206
|
/**
|
|
189
207
|
* Acknowledge a message.
|
|
190
208
|
*
|
|
@@ -228,9 +246,10 @@ declare class AmqpClient {
|
|
|
228
246
|
* - Decrease the reference count on the shared connection
|
|
229
247
|
* - Close the connection if this was the last client using it
|
|
230
248
|
*
|
|
231
|
-
*
|
|
249
|
+
* Both steps run regardless of each other's outcome; if both fail, the
|
|
250
|
+
* errors are wrapped in an AggregateError.
|
|
232
251
|
*/
|
|
233
|
-
close():
|
|
252
|
+
close(): ResultAsync<void, TechnicalError>;
|
|
234
253
|
/**
|
|
235
254
|
* Reset connection singleton cache (for testing only)
|
|
236
255
|
* @internal
|
|
@@ -311,6 +330,30 @@ type Logger = {
|
|
|
311
330
|
error(message: string, context?: LoggerContext): void;
|
|
312
331
|
};
|
|
313
332
|
//#endregion
|
|
333
|
+
//#region src/parsing.d.ts
|
|
334
|
+
/**
|
|
335
|
+
* Parse a `Buffer` as JSON, mapping any `JSON.parse` exception to the
|
|
336
|
+
* caller-supplied error type.
|
|
337
|
+
*
|
|
338
|
+
* Use this in consume / reply paths where a parse failure is a typed value,
|
|
339
|
+
* not a thrown exception — the caller decides how to translate the raw error
|
|
340
|
+
* into a domain-level error (e.g. {@link TechnicalError}).
|
|
341
|
+
*
|
|
342
|
+
* @typeParam E - The error type produced by `errorFn`.
|
|
343
|
+
* @param buffer - The raw message body to parse.
|
|
344
|
+
* @param errorFn - Callback invoked with the underlying `JSON.parse` error.
|
|
345
|
+
* @returns A `Result` containing the parsed `unknown` value or the mapped error.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```typescript
|
|
349
|
+
* const parsed = safeJsonParse(
|
|
350
|
+
* msg.content,
|
|
351
|
+
* (error) => new TechnicalError("Failed to parse JSON", error),
|
|
352
|
+
* );
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
declare function safeJsonParse<E>(buffer: Buffer, errorFn: (raw: unknown) => E): Result<unknown, E>;
|
|
356
|
+
//#endregion
|
|
314
357
|
//#region src/setup.d.ts
|
|
315
358
|
/**
|
|
316
359
|
* Setup AMQP topology (exchanges, queues, and bindings) from a contract definition.
|
|
@@ -438,5 +481,5 @@ declare function recordLateRpcReply(provider: TelemetryProvider, reason: "unknow
|
|
|
438
481
|
*/
|
|
439
482
|
declare function _resetTelemetryCacheForTesting(): void;
|
|
440
483
|
//#endregion
|
|
441
|
-
export { AmqpClient, type AmqpClientOptions, type ConsumeCallback, type ConsumerOptions, DEFAULT_CONNECT_TIMEOUT_MS, type Logger, type LoggerContext, MessageValidationError, MessagingSemanticConventions, type PublishOptions, TechnicalError, type TelemetryProvider, _getConnectionCountForTesting, _resetConnectionsForTesting, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordLateRpcReply, recordPublishMetric, setupAmqpTopology, startConsumeSpan, startPublishSpan };
|
|
484
|
+
export { AmqpClient, type AmqpClientOptions, type ConsumeCallback, type ConsumerOptions, DEFAULT_CONNECT_TIMEOUT_MS, type Logger, type LoggerContext, MessageValidationError, MessagingSemanticConventions, type PublishOptions, TechnicalError, type TelemetryProvider, _getConnectionCountForTesting, _resetConnectionsForTesting, _resetTelemetryCacheForTesting, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, recordLateRpcReply, recordPublishMetric, safeJsonParse, setupAmqpTopology, startConsumeSpan, startPublishSpan };
|
|
442
485
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/errors.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/logger.ts","../src/setup.ts","../src/telemetry.ts"],"mappings":";;;;;;;;;;;;;cAMa,cAAA,SAAuB,KAAA;EAAA,SAGP,KAAA;cADzB,OAAA,UACyB,KAAA;AAAA;;;;;;;;;AAuB7B;cAAa,sBAAA,SAA+B,KAAA;EAAA,SAExB,MAAA;EAAA,SACA,MAAA;cADA,MAAA,UACA,MAAA;AAAA;;;;;AA7BpB;;;;;;;cCwCa,0BAAA;;;;ADdb;;;;;;;;KCwCY,iBAAA;EACV,IAAA,EAAM,aAAA;EACN,iBAAA,GAAoB,4BAAA;EACpB,cAAA,GAAiB,OAAA,CAAQ,iBAAA;EACzB,gBAAA;AAAA;;AA9BF;;KAoCY,eAAA,IAAmB,GAAA,EAAK,cAAA,mBAAiC,OAAA;;;AAVrE
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/errors.ts","../src/amqp-client.ts","../src/connection-manager.ts","../src/logger.ts","../src/parsing.ts","../src/setup.ts","../src/telemetry.ts"],"mappings":"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"}
|