X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=src%2Fcommon.js;h=7d0d3180bba4dec235c0bf49053b00f5b09dc1e6;hb=1c37a7c533a5530390489ea9a49dcca492db1074;hp=0cacc3bf086f2b57f0b10db1ef289c6be7961e15;hpb=28de4364128a4b03918a8cbe868009b5d427220a;p=websub-hub diff --git a/src/common.js b/src/common.js index 0cacc3b..7d0d318 100644 --- a/src/common.js +++ b/src/common.js @@ -19,46 +19,74 @@ const randomBytesAsync = promisify(randomBytes); /** * 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 possibly an array + * @returns {Array} x or [x] + */ +const ensureArray = (x) => { + if (x === undefined) { + return []; + } + if (!Array.isArray(x)) { + return Array(x); + } + return x; +}; + + /** * 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 = ``; + } + 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; }; @@ -66,7 +94,7 @@ const axiosResponseLogData = (res) => { /** * Fallback values, if not configured. - * @returns {Object} + * @returns {object} object */ const topicLeaseDefaults = () => { return Object.freeze({ @@ -79,10 +107,10 @@ const topicLeaseDefaults = () => { /** * 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; @@ -91,42 +119,43 @@ const attemptRetrySeconds = (attempt, retryBackoffSeconds = [60, 120, 360, 1440, } 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) { @@ -139,7 +168,8 @@ module.exports = { ...common, arrayChunk, attemptRetrySeconds, - axiosResponseLogData, + gotResponseLogData, + ensureArray, freezeDeep, logTruncate, randomBytesAsync,