Skip to content

Release 4.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 12 Jun 14:50

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?) and session.exchange(name, type, options?) declare and return reconnect-safe handles; pass broker arguments via options.args (#209)
    • Shorthand exchange factories: directExchange(), fanoutExchange(), topicExchange(), headersExchange()
    • session.onconnect / session.onfailed lifecycle hooks; session.closed; session.stop() cancels reconnection, clears subscriptions, and closes the connection
    • Lifecycle transitions log via the configured logger (info on connect, warn on disconnect, error when reconnect gives up), prefixed with AMQPSession[${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 from session.queue(), with publish(), subscribe(), get(), bind(), unbind(), purge(), delete() (#186)
    • subscribe(callback) / subscribe(params, callback) auto-acks after the callback returns, nacks and requeues on throw; call msg.ack() / msg.nack() to override, or pass { noAck: true } to skip; requeueOnNack controls requeue on error (#189)
    • subscribe() / subscribe(params) async-iterator form auto-acks the previous message as the loop advances; the last message (after break) is left unacked (#189)
    • Subscriptions survive reconnection automatically
    • bind() / unbind() accept an AMQPExchange handle or an exchange name
    • publish() defaults deliveryMode to 2 (persistent) so messages survive a broker restart
    • Server-named queues (declared with "") are not tracked for auto-recovery — re-declare them in an onconnect handler, which runs after each reconnect (#230)
  • AMQPExchange — reconnect-safe exchange handle from session.exchange(), with publish(), bind(), unbind(), delete(); publish() defaults deliveryMode to 2 (#186)
  • AMQPSubscription — stable consumer handle across reconnections: exposes channel, consumerTag, and cancel(); cancel() is best-effort and never throws — closed channels and connections count as success (#208)
  • AMQPGeneratorSubscription — extends AMQPSubscription with AsyncIterable<AMQPMessage> support
  • AMQPQueue.consumeOne({ timeout }) — one-shot consume that resolves with a single message or rejects on timeout (#212)
    • Dedicated channel with prefetch: 1 so 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
  • 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 by AMQPSession on reconnect when created via session.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 AMQPMessage and returns the response body
  • Session-level RPC convenience methods (#191)
    • session.rpcCall(queue, body, options?) — one-shot call (recommended for most uses)
    • session.rpcClient() — reusable AMQPRPCClient for high throughput
    • session.rpcServer(queue, handler, prefetch?) — create and start an AMQPRPCServer
  • 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) and builtinCoders (gzip, deflate) ship ready to spread into the registry; BuiltinParsers / BuiltinCoders describe their shape
    • defaultContentType / defaultContentEncoding session options apply codecs to publishes that don't set them
    • Wired into AMQPClient, AMQPSession, AMQPQueue, AMQPExchange, AMQPSubscription, AMQPRPCClient, and AMQPRPCServer
    • CodecMode generic ("plain" | "codec") threads through session, queue, exchange, RPC, and message types so the body type is inferred at compile time
    • AMQPMessage<CodecMode> exposes msg.body as Uint8Array in "plain" mode and the decoded value in "codec" mode
    • Type-safe publish overloads use PublishBody<C> instead of unknown; misconfigured publishes fail fast at the call site
    • Decode errors surface as plain Error (not AMQPError); subscribe honors requeueOnNack for them
  • AMQPSessionLike, AMQPQueueLike, AMQPExchangeLike, AMQPSubscriptionLike — minimum surface interfaces for mocking in tests (#209)
  • QueueSubscribeParams — type combining ConsumeParams with prefetch? and requeueOnNack? (default true) (#189)
  • QueuePublishOptions / ExchangePublishOptions — publish option types extending AMQPProperties with a confirm? flag; ExchangePublishOptions adds routingKey?
  • ondisconnect hook on AMQPBaseClient (TCP and WebSocket) — fires when the connection drops

Breaking

  • AMQPChannel.queue() removed (#186). Use ch.queueDeclare() with low-level channel methods, or session.queue() for the high-level API. See the migration guide below.
  • AMQPQueue is now a session-only class — no longer returned by channel methods, no longer accepts a channel in its constructor. (#186)
  • AMQPQueue is no longer re-exported from AMQPClient or AMQPWebSocketClient. 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()