UNPKG

@cloudamqp/amqp-client

Version:

AMQP 0-9-1 client, both for browsers (WebSocket) and node (TCP Socket)

211 lines 8.41 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AMQPQueue = void 0; const amqp_subscription_js_1 = require("./amqp-subscription.js"); const amqp_codec_registry_js_1 = require("./amqp-codec-registry.js"); class AMQPQueue { constructor(session, name) { this.subscriptions = new Set(); this.session = session; this.name = name; } async publish(body, options = {}) { const { confirm = true, mandatory = false, ...properties } = options; const defaults = {}; if (this.session.defaultContentType) defaults.contentType = this.session.defaultContentType; if (this.session.defaultContentEncoding) defaults.contentEncoding = this.session.defaultContentEncoding; const encoded = await (0, amqp_codec_registry_js_1.serializeAndEncode)(this.session.parsers ?? {}, this.session.coders ?? {}, body, properties, defaults); if (encoded.properties.deliveryMode === undefined) encoded.properties.deliveryMode = 2; if (confirm) { const ch = await this.session.getConfirmChannel(); await ch.basicPublish("", this.name, encoded.body, encoded.properties, mandatory); } else { await this.session.withOpsChannel(async (ch) => { await ch.basicPublish("", this.name, encoded.body, encoded.properties, mandatory); }); } return this; } async subscribe(params, callback) { if (typeof params === "function") [callback, params] = [params, undefined]; const { prefetch, requeueOnNack = true, ...consumeParams } = params ?? {}; const autoAck = !consumeParams.noAck; if (autoAck) consumeParams.noAck = false; const parsers = this.session.parsers; const coders = this.session.coders; const logger = this.session.logger; const wrappedCallback = wrapCallbackWithAutoDecodeAndAck(callback, { parsers, coders, autoAck, requeueOnNack, logger, }); const def = { queueName: this.name, consumeParams, requeueOnNack, ...(wrappedCallback !== undefined && { callback: wrappedCallback }), ...(prefetch !== undefined && { prefetch }), ...(parsers && { parsers }), ...(coders && { coders }), }; const consumer = await this.openConsumer(def); const sub = wrappedCallback ? new amqp_subscription_js_1.AMQPSubscription(consumer, def) : new amqp_subscription_js_1.AMQPGeneratorSubscription(consumer, def); this.subscriptions.add(sub); sub.onCancel = () => { this.subscriptions.delete(sub); }; return sub; } async get(params) { const msg = await this.session.withOpsChannel((ch) => ch.basicGet(this.name, params)); if (!msg) return null; if (this.session.parsers || this.session.coders) { await (0, amqp_codec_registry_js_1.decodeMessage)(msg, this.session.parsers ?? {}, this.session.coders ?? {}); } return msg; } async consumeOne(options = {}) { const { timeout } = options; const ch = await this.session.openChannel(); await ch.basicQos(1); return new Promise((resolve, reject) => { let timer; let settled = false; let consumer; const cleanup = async () => { if (timer) clearTimeout(timer); try { if (consumer && !this.session.closed) await consumer.cancel(); } catch { } if (!ch.closed) await ch.close().catch(() => { }); }; const finish = async (settle) => { if (settled) return; settled = true; await cleanup(); settle(); }; ch.basicConsume(this.name, { noAck: false }, async (msg) => { try { if (settled) { await msg.nack(true).catch(() => { }); return; } if (this.session.parsers || this.session.coders) { await (0, amqp_codec_registry_js_1.decodeMessage)(msg, this.session.parsers ?? {}, this.session.coders ?? {}); } await msg.ack(); await finish(() => resolve(msg)); } catch (err) { await finish(() => reject(err)); } }) .then((c) => { consumer = c; if (settled) { void cleanup(); return; } c.wait() .then(() => void finish(() => reject(new Error("consumeOne: consumer closed before delivery")))) .catch((err) => void finish(() => reject(err))); if (timeout !== undefined) { timer = setTimeout(() => void finish(() => reject(new Error(`consumeOne timed out after ${timeout}ms`))), timeout); } }) .catch((err) => void finish(() => reject(err))); }); } async bind(exchange, routingKey = "", args = {}) { const exchangeName = typeof exchange === "string" ? exchange : exchange.name; await this.session.withOpsChannel((ch) => ch.queueBind(this.name, exchangeName, routingKey, args)); return this; } async unbind(exchange, routingKey = "", args = {}) { const exchangeName = typeof exchange === "string" ? exchange : exchange.name; await this.session.withOpsChannel((ch) => ch.queueUnbind(this.name, exchangeName, routingKey, args)); return this; } async purge() { return this.session.withOpsChannel((ch) => ch.queuePurge(this.name)); } async delete(params) { const result = await this.session.withOpsChannel((ch) => ch.queueDelete(this.name, params)); this.cancelAll(); this.session.removeQueue(this.name); return result; } async recover() { for (const sub of this.subscriptions) { try { const consumer = await this.openConsumer(sub.def); sub.setConsumer(consumer); this.session.logger?.debug(`Recovered consumer for queue: ${this.name}`); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); this.session.logger?.warn(`Failed to recover consumer for queue ${this.name}:`, error.message); } } } cancelAll() { for (const sub of this.subscriptions) { sub.cancel().catch(() => { }); } this.subscriptions.clear(); } async openConsumer(def) { const ch = await this.session.openChannel(); if (def.prefetch !== undefined) { await ch.basicQos(def.prefetch); } return def.callback ? ch.basicConsume(def.queueName, def.consumeParams, def.callback) : ch.basicConsume(def.queueName, def.consumeParams); } } exports.AMQPQueue = AMQPQueue; function wrapCallbackWithAutoDecodeAndAck(callback, opts) { if (!callback) return undefined; if (!opts.autoAck) { return async (msg) => { try { if (opts.parsers || opts.coders) await (0, amqp_codec_registry_js_1.decodeMessage)(msg, opts.parsers ?? {}, opts.coders ?? {}); await callback(msg); } catch (err) { opts.logger?.error("Consumer callback error", err); } }; } return async (msg) => { try { if (opts.parsers || opts.coders) await (0, amqp_codec_registry_js_1.decodeMessage)(msg, opts.parsers ?? {}, opts.coders ?? {}); await callback(msg); await msg.ack(); } catch { await msg.nack(opts.requeueOnNack); } }; } //# sourceMappingURL=amqp-queue.js.map