UNPKG

rabbitmq-client

Version:
167 lines (166 loc) 5.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EncoderStream = exports.READY_STATE = void 0; exports.createDeferred = createDeferred; exports.expBackoff = expBackoff; exports.pick = pick; exports.camelCase = camelCase; exports.createAsyncReader = createAsyncReader; exports.expectEvent = expectEvent; exports.recaptureAndThrow = recaptureAndThrow; const exception_1 = require("./exception"); const node_stream_1 = require("node:stream"); const node_util_1 = require("node:util"); /** @internal */ var READY_STATE; (function (READY_STATE) { READY_STATE[READY_STATE["CONNECTING"] = 0] = "CONNECTING"; READY_STATE[READY_STATE["OPEN"] = 1] = "OPEN"; READY_STATE[READY_STATE["CLOSING"] = 2] = "CLOSING"; READY_STATE[READY_STATE["CLOSED"] = 3] = "CLOSED"; })(READY_STATE || (exports.READY_STATE = READY_STATE = {})); /** @internal */ function createDeferred(noUncaught) { const dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); if (noUncaught) dfd.promise.catch(() => { }); return dfd; } /** * Calculate exponential backoff/retry delay. * Where attempts >= 1, exp > 1 * @example expBackoff(1000, 30000, attempts) * --------------------------------- * attempts | possible delay * ----------+---------------------- * 1 | 1000 to 2000 * 2 | 1000 to 4000 * 3 | 1000 to 8000 * 4 | 1000 to 16000 * 5 | 1000 to 30000 * --------------------------------- * Attempts required before max delay is possible = Math.ceil(Math.log(high/step) / Math.log(exp)) * @internal */ function expBackoff(step, high, attempts, exp = 2) { const slots = Math.ceil(Math.min(high / step, Math.pow(exp, attempts))); const max = Math.min(slots * step, high); return Math.floor(Math.random() * (max - step) + step); } /** @internal */ function pick(src, keys) { const dest = {}; for (const key of keys) { dest[key] = src[key]; } return dest; } /** @internal */ function camelCase(string) { const parts = string.match(/[^.|-]+/g); if (!parts) return string; return parts.reduce((acc, word, index) => { return acc + (index ? word.charAt(0).toUpperCase() + word.slice(1) : word); }); } /** * Wrap Readable.read() to ensure that it either resolves with a Buffer of the * requested length, waiting for more data when necessary, or is rejected. * Assumes only a single pending read at a time. * See also: https://nodejs.org/api/stream.html#readablereadsize * @internal */ function createAsyncReader(socket) { let bytes; let cb; function _read() { if (!cb) return; const buf = socket.read(bytes); if (!buf && socket.readable) return; // wait for readable OR close if (buf?.byteLength !== bytes) { cb(new exception_1.AMQPConnectionError('READ_END', 'stream ended before all bytes received'), buf); } else { cb(null, buf); } cb = undefined; } socket.on('close', _read); socket.on('readable', _read); return (0, node_util_1.promisify)((_bytes, _cb) => { bytes = _bytes; cb = _cb; _read(); }); } /** * Consumes Iterators (like from a generator-fn). * Writes Buffers (or whatever the iterators produce) to the output stream * @internal */ class EncoderStream extends node_stream_1.Writable { _cur; _out; constructor(out) { super({ objectMode: true }); this._out = out; this._loop = this._loop.bind(this); out.on('drain', this._loop); } writeAsync = (0, node_util_1.promisify)(this.write); _destroy(err, cb) { this._out.removeListener('drain', this._loop); if (this._cur) { this._cur[1](err); this._cur = undefined; } cb(err); } _write(it, enc, cb) { this._cur = [it, cb]; this._loop(); } /** Squeeze the current iterator until it's empty, but respect back-pressure. */ _loop() { if (!this._cur) return; const [it, cb] = this._cur; let res; let ok = !this._out.writableNeedDrain; try { // if Nagle's algorithm is enabled, this will reduce latency this._out.cork(); while (ok && (res = it.next()) && !res.done) ok = this._out.write(res.value); } catch (err) { return cb(err); } finally { this._out.uncork(); // TODO consider this: //process.nextTick(() => this._out.uncork()) } if (res?.done) { this._cur = undefined; cb(); } } } exports.EncoderStream = EncoderStream; /** @internal */ function expectEvent(emitter, name) { return new Promise((resolve) => { emitter.once(name, resolve); }); } /** @internal */ function recaptureAndThrow(err) { Error.captureStackTrace(err, recaptureAndThrow); throw err; }