/**
* The HMAC hashes we are willing to support.
- * @param {String} algorithm
- * @returns {Boolean}
+ * @param {string} algorithm potential sha* algorithm
+ * @returns {boolean} is supported
*/
const validHash = (algorithm) => getHashes()
- .filter((h) => h.match(/^sha[0-9]+$/))
+ .filter((h) => h.match(/^sha\d+$/))
.includes(algorithm);
/**
* Return an array containing x if x is not an array.
- * @param {*} x
+ * @param {*} x possibly an array
+ * @returns {Array} x or [x]
*/
const ensureArray = (x) => {
if (x === undefined) {
/**
* Recursively freeze an object.
- * @param {Object} o
- * @returns {Object}
+ * @param {object} o object
+ * @returns {object} frozen object
*/
const freezeDeep = (o) => {
Object.freeze(o);
Object.getOwnPropertyNames(o).forEach((prop) => {
- if (Object.hasOwnProperty.call(o, prop)
+ if (Object.hasOwn(o, prop)
&& ['object', 'function'].includes(typeof o[prop])
&& !Object.isFrozen(o[prop])) {
return freezeDeep(o[prop]);
}
});
return o;
-}
+};
/**
- * Pick out useful axios response fields.
- * @param {*} res
- * @returns
+ * Pick out useful got response fields.
+ * @param {*} res response
+ * @returns {object} winnowed response
*/
-const axiosResponseLogData = (res) => {
+const gotResponseLogData = (res) => {
const data = common.pick(res, [
- 'status',
- 'statusText',
+ 'statusCode',
+ 'statusMessage',
'headers',
- 'elapsedTimeMs',
- 'data',
+ 'body',
+ 'error',
]);
- if (data.data) {
- data.data = logTruncate(data.data, 100);
+ if (typeof res.body === 'string') {
+ data.body = logTruncate(data.body, 100);
+ } else if (res.body instanceof Buffer) {
+ data.body = `<Buffer ${res.body.byteLength} bytes>`;
+ }
+ if (res?.timings?.phases?.total) {
+ data.elapsedTimeMs = res.timings.phases.total;
+ }
+ if (res?.redirectUrls?.length) {
+ data.redirectUrls = res.redirectUrls;
+ }
+ if (res?.retryCount) {
+ data.retryCount = res.retryCount;
}
return data;
};
/**
* Fallback values, if not configured.
- * @returns {Object}
+ * @returns {object} object
*/
const topicLeaseDefaults = () => {
return Object.freeze({
/**
* Pick from a range, constrained, with some fuzziness.
- * @param {Number} attempt
- * @param {Number[]} delays
- * @param {Number} jitter
- * @returns {Number}
+ * @param {number} attempt attempt number
+ * @param {number[]=} retryBackoffSeconds array of indexed delays
+ * @param {number=} jitter vary backoff by up to this fraction additional
+ * @returns {number} seconds to delay retry
*/
const attemptRetrySeconds = (attempt, retryBackoffSeconds = [60, 120, 360, 1440, 7200, 43200, 86400], jitter = 0.618) => {
const maxAttempt = retryBackoffSeconds.length - 1;
} else if (attempt > maxAttempt) {
attempt = maxAttempt;
}
- // eslint-disable-next-line security/detect-object-injection
+
let seconds = retryBackoffSeconds[attempt];
seconds += Math.floor(Math.random() * seconds * jitter);
return seconds;
-}
+};
/**
* Return array items split as an array of arrays of no more than per items each.
- * @param {Array} array
- * @param {Number} per
+ * @param {Array} array items
+ * @param {number} per chunk size
+ * @returns {Array[]} array of chunks
*/
const arrayChunk = (array, per = 1) => {
const nChunks = Math.ceil(array.length / per);
return Array.from(Array(nChunks), (_, i) => array.slice(i * per, (i + 1) * per));
-}
+};
/**
* Be paranoid about blowing the stack when pushing to an array.
- * @param {Array} dst
- * @param {Array} src
+ * @param {Array} dst destination array
+ * @param {Array} src source array
*/
const stackSafePush = (dst, src) => {
const jsEngineMaxArguments = 2**16; // Current as of Node 12
arrayChunk(src, jsEngineMaxArguments).forEach((items) => {
Array.prototype.push.apply(dst, items);
});
-}
+};
/**
* Limit length of string to keep logs sane
- * @param {String} str
- * @param {Number} len
- * @returns {String}
+ * @param {string} str string
+ * @param {number} len max length
+ * @returns {string} truncated string
*/
const logTruncate = (str, len) => {
if (typeof str !== 'string' || str.toString().length <= len) {
return str.toString().slice(0, len) + `... (${str.toString().length} bytes)`;
};
+const nop = () => undefined;
+
module.exports = {
...common,
arrayChunk,
attemptRetrySeconds,
- axiosResponseLogData,
+ gotResponseLogData,
ensureArray,
freezeDeep,
logTruncate,
+ nop,
randomBytesAsync,
stackSafePush,
topicLeaseDefaults,