logged scope now includes library and version
[squeep-indieauth-helper] / lib / common.js
1 'use strict';
2
3 const path = require('path');
4 const { name: packageName, version: packageVersion } = require('../package');
5
6 const libraryIdentifier = `${packageName}@${packageVersion}`;
7
8 /**
9 * Return a function which combines a part of the filename with a scope, for use in logging.
10 * @param {string} filename
11 */
12 const fileScope = (filename) => {
13 const shortFilename = path.basename(filename, '.js');
14 const fScope = (shortFilename === 'index') ? path.basename(path.dirname(filename)) : shortFilename;
15 return (scope) => [libraryIdentifier, fScope, scope].join(':');
16 }
17
18
19 /**
20 * Pick out useful axios response fields.
21 * @param {AxiosResponse} res
22 * @returns {Object}
23 */
24 const axiosResponseLogData = (res) => {
25 const data = pick(res, [
26 'status',
27 'statusText',
28 'headers',
29 'elapsedTimeMs',
30 'data',
31 ]);
32 if (data.data) {
33 data.data = logTruncate(data.data, 100);
34 }
35 return data;
36 };
37
38
39 /**
40 * Limit length of string to keep logs sane
41 * @param {String} str
42 * @param {Number} len
43 * @returns {String}
44 */
45 const logTruncate = (str, len) => {
46 if (typeof str !== 'string' || str.toString().length <= len) {
47 return str;
48 }
49 return str.toString().slice(0, len) + `... (${str.toString().length} bytes)`;
50 };
51
52
53 /**
54 * Return a new object with selected props.
55 * @param {Object} obj
56 * @param {String[]} props
57 */
58 const pick = (obj, props) => {
59 return props.reduce((acc, prop) => {
60 if (prop in obj) {
61 acc[prop] = obj[prop]; // eslint-disable-line security/detect-object-injection
62 }
63 return acc;
64 }, {});
65 };
66
67
68 /**
69 * Return a set containing non-shared items between two sets.
70 * @param {Set} a
71 * @param {Set} b
72 * @returns {Set}
73 */
74 const setSymmetricDifference = (a, b) => {
75 const d = new Set(a);
76 for (const x of b) {
77 if (d.has(x)) {
78 d.delete(x);
79 } else {
80 d.add(x);
81 }
82 }
83 return d;
84 };
85
86
87 /**
88 * URL objects have weird names.
89 * @param {String} component
90 * @returns {String}
91 */
92 const properURLComponentName = (component) => {
93 // eslint-disable-next-line security/detect-object-injection
94 return {
95 hash: 'fragment',
96 protocol: 'scheme',
97 }[component] || component;
98 }
99
100
101 /**
102 * Encodes single-level object as form data string.
103 * @param {Object} data
104 */
105 const formData = (data) => {
106 const formData = new URLSearchParams();
107 Object.entries(data).forEach(([name, value]) => formData.set(name, value));
108 return formData.toString();
109 };
110
111
112 module.exports = {
113 fileScope,
114 axiosResponseLogData,
115 logTruncate,
116 pick,
117 setSymmetricDifference,
118 properURLComponentName,
119 formData,
120 };