X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=src%2Fcommon.js;h=0d6500c44eaaf7b201e96ecef698931761423005;hb=3ca7fccb306d0b23626befc3791ffa360b3db1e7;hp=55a0807b00757e2c2fa0fe8d50e7c22701b0bacf;hpb=cab7ebc31583981d0c235039afdfc9d63e730f02;p=websub-hub diff --git a/src/common.js b/src/common.js index 55a0807..0d6500c 100644 --- a/src/common.js +++ b/src/common.js @@ -19,17 +19,18 @@ 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 + * @param {*} x possibly an array + * @returns {Array} x or [x] */ const ensureArray = (x) => { if (x === undefined) { @@ -44,37 +45,48 @@ const ensureArray = (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; }; @@ -82,7 +94,7 @@ const axiosResponseLogData = (res) => { /** * Fallback values, if not configured. - * @returns {Object} + * @returns {object} object */ const topicLeaseDefaults = () => { return Object.freeze({ @@ -95,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; @@ -107,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) { @@ -151,14 +164,17 @@ const logTruncate = (str, 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,