Added
AMQPSession— high-level client with automatic reconnection and consumer recovery (#185, #186)AMQPSession.connect(url, options?)picks TCP or WebSocket transport from the URL scheme (amqp:///amqps://→ TCP;ws:///wss://→ WebSocket)- Exponential backoff via
reconnectInterval,maxReconnectInterval,backoffMultiplier,maxRetries session.queue(name, options?)andsession.exchange(name, type, options?)declare and return reconnect-safe handles; pass broker arguments viaoptions.args(#209)- Shorthand exchange factories:
directExchange(),fanoutExchange(),topicExchange(),headersExchange() session.onconnect/session.onfailedlifecycle hooks;session.closed;session.stop()cancels reconnection, clears subscriptions, and closes the connection- Lifecycle transitions log via the configured logger (
infoon connect,warnon disconnect,errorwhen reconnect gives up), prefixed withAMQPSession[${name}]:when the URL carries?name=(#219) - Session ops (declare, bind/unbind, etc.) are serialized through a mutex so concurrent declarations on the shared ops channel can't interleave (#207)
AMQPQueue— reconnect-safe queue handle fromsession.queue(), withpublish(),subscribe(),get(),bind(),unbind(),purge(),delete()(#186)subscribe(callback)/subscribe(params, callback)auto-acks after the callback returns, nacks and requeues on throw; callmsg.ack()/msg.nack()to override, or pass{ noAck: true }to skip;requeueOnNackcontrols requeue on error (#189)subscribe()/subscribe(params)async-iterator form auto-acks the previous message as the loop advances; the last message (afterbreak) is left unacked (#189)- Subscriptions survive reconnection automatically
bind()/unbind()accept anAMQPExchangehandle or an exchange namepublish()defaultsdeliveryModeto2(persistent) so messages survive a broker restart- Server-named queues (declared with
"") are not tracked for auto-recovery — re-declare them in anonconnecthandler, which runs after each reconnect (#230)
AMQPExchange— reconnect-safe exchange handle fromsession.exchange(), withpublish(),bind(),unbind(),delete();publish()defaultsdeliveryModeto2(#186)AMQPSubscription— stable consumer handle across reconnections: exposeschannel,consumerTag, andcancel();cancel()is best-effort and never throws — closed channels and connections count as success (#208)AMQPGeneratorSubscription— extendsAMQPSubscriptionwithAsyncIterable<AMQPMessage>supportAMQPQueue.consumeOne({ timeout })— one-shot consume that resolves with a single message or rejects on timeout (#212)- Dedicated channel with
prefetch: 1so the broker holds the queue at one in-flight delivery - Acks the returned message before resolving; late deliveries are nacked with requeue; rejects when the consumer, channel, or connection closes first
- Dedicated channel with
AMQPRPCClient— reusable RPC client using direct reply-to for request-response (#191)start()to listen for responses,call(queue, body, options?)to publish a request and await its response,close()to reject pending calls and clean up- Per-call
timeout, automatic correlation ID tracking; recovered byAMQPSessionon reconnect when created viasession.rpcClient()
AMQPRPCServer— RPC server that consumes from a queue and replies to each caller (#191)- Session-level subscribe for automatic consumer recovery; handler receives the full
AMQPMessageand returns the response body
- Session-level subscribe for automatic consumer recovery; handler receives the full
- Session-level RPC convenience methods (#191)
session.rpcCall(queue, body, options?)— one-shot call (recommended for most uses)session.rpcClient()— reusableAMQPRPCClientfor high throughputsession.rpcServer(queue, handler, prefetch?)— create and start anAMQPRPCServer
AMQPCodecRegistry— opt-in automatic encoding/decoding of message bodies by content-type (#192)- Builtin codec constants for JSON, text, and raw bytes; register your own for other content-types
builtinParsers(JSON, text) andbuiltinCoders(gzip, deflate) ship ready to spread into the registry;BuiltinParsers/BuiltinCodersdescribe their shapedefaultContentType/defaultContentEncodingsession options apply codecs to publishes that don't set them- Wired into
AMQPClient,AMQPSession,AMQPQueue,AMQPExchange,AMQPSubscription,AMQPRPCClient, andAMQPRPCServer CodecModegeneric ("plain" | "codec") threads through session, queue, exchange, RPC, and message types so the body type is inferred at compile timeAMQPMessage<CodecMode>exposesmsg.bodyasUint8Arrayin"plain"mode and the decoded value in"codec"mode- Type-safe publish overloads use
PublishBody<C>instead ofunknown; misconfigured publishes fail fast at the call site - Decode errors surface as plain
Error(notAMQPError); subscribe honorsrequeueOnNackfor them
AMQPSessionLike,AMQPQueueLike,AMQPExchangeLike,AMQPSubscriptionLike— minimum surface interfaces for mocking in tests (#209)QueueSubscribeParams— type combiningConsumeParamswithprefetch?andrequeueOnNack?(defaulttrue) (#189)QueuePublishOptions/ExchangePublishOptions— publish option types extendingAMQPPropertieswith aconfirm?flag;ExchangePublishOptionsaddsroutingKey?ondisconnecthook onAMQPBaseClient(TCP and WebSocket) — fires when the connection drops
Breaking
AMQPChannel.queue()removed (#186). Usech.queueDeclare()with low-level channel methods, orsession.queue()for the high-level API. See the migration guide below.AMQPQueueis now a session-only class — no longer returned by channel methods, no longer accepts a channel in its constructor. (#186)AMQPQueueis no longer re-exported fromAMQPClientorAMQPWebSocketClient. Import from the main package entry point instead. (#186)- Minimum supported Node version is now 18
Migration guide
The v3 AMQPQueue was tied to a single channel. In v4, AMQPQueue is a session-level handle that is reconnect-safe.
If you were using ch.queue():
-const ch = await conn.channel()
-const q = await ch.queue("my-queue")
-await q.publish("hello")
-const consumer = await q.subscribe({ noAck: false }, (msg) => msg.ack())
-const msg = await q.get()
-await q.bind("amq.topic", "routing.key")
-await q.delete()
+// Low-level (no reconnection)
+const ch = await conn.channel()
+const { name } = await ch.queueDeclare("my-queue")
+await ch.basicPublish("", name, "hello")
+const consumer = await ch.basicConsume(name, { noAck: false }, (msg) => msg.ack())
+const msg = await ch.basicGet(name)
+await ch.queueBind(name, "amq.topic", "routing.key")
+await ch.queueDelete(name)
+// High-level (automatic reconnection)
+const session = await AMQPSession.connect("amqp://localhost")
+const q = await session.queue("my-queue")
+await q.publish("hello")
+const sub = await q.subscribe({ noAck: false }, (msg) => msg.ack())
+const msg = await q.get()
+await q.bind("amq.topic", "routing.key")
+await q.delete()