update dependencies and devDependencies, fix lint issues
[websub-hub] / src / common.js
index 55a0807b00757e2c2fa0fe8d50e7c22701b0bacf..7d0d3180bba4dec235c0bf49053b00f5b09dc1e6 100644 (file)
@@ -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 = `<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;
 };
@@ -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) {
@@ -155,7 +168,7 @@ module.exports = {
   ...common,
   arrayChunk,
   attemptRetrySeconds,
-  axiosResponseLogData,
+  gotResponseLogData,
   ensureArray,
   freezeDeep,
   logTruncate,