fix moved code, adapt imports, revert trivial manual change
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { logger } from "../src/common/utils.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { authenticate } from "../src/users.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { logger } from "../src/common/utils.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { deleteUser } from "../src/common/database.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
retrieveSecondaryMessage,
|
||||
} from "../src/common/error.js";
|
||||
import { parseBoolean } from "../src/common/ops.js";
|
||||
import { parseArray } from "../src/common/utils.js";
|
||||
import { parseArray } from "../src/common/ops.js";
|
||||
import { renderError } from "../src/common/render.js";
|
||||
import { fetchRepo } from "../src/fetchers/repo.js";
|
||||
import { isLocaleAvailable } from "../src/translations.js";
|
||||
@@ -110,13 +110,12 @@ export default async (req, res) => {
|
||||
showStats.includes("issues_authored"),
|
||||
showStats.includes("issues_commented"),
|
||||
);
|
||||
const repoData = await fetchRepo(username, repo);
|
||||
|
||||
const cacheSeconds = resolveCacheSeconds({
|
||||
requested: parseInt(cache_seconds, 10),
|
||||
def: CONSTANTS.PIN_CARD_CACHE_SECONDS,
|
||||
min: CONSTANTS.FOUR_HOURS,
|
||||
max: CONSTANTS.TEN_DAY,
|
||||
def: CACHE_TTL.PIN_CARD.DEFAULT,
|
||||
min: CACHE_TTL.PIN_CARD.MIN,
|
||||
max: CACHE_TTL.PIN_CARD.MAX,
|
||||
});
|
||||
|
||||
setCacheHeaders(res, cacheSeconds);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { logger } from "../src/common/utils.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
import { getUserAccess } from "../src/common/database.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
|
||||
import { logger } from "../src/common/utils.js";
|
||||
import { logger } from "../src/common/log.js";
|
||||
|
||||
/**
|
||||
* @param {any} req The request.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { encodeHTML } from "../common/html.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { icons } from "../common/icons.js";
|
||||
import { clampValue, parseEmojis } from "../common/ops.js";
|
||||
import { buildSearchFilter } from "../common/utils.js";
|
||||
import { buildSearchFilter } from "../common/ops.js";
|
||||
import {
|
||||
flexLayout,
|
||||
measureText,
|
||||
|
||||
@@ -7,7 +7,7 @@ import { kFormatter } from "../common/fmt.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
import { icons, rankIcon } from "../common/icons.js";
|
||||
import { clampValue } from "../common/ops.js";
|
||||
import { buildSearchFilter } from "../common/utils.js";
|
||||
import { buildSearchFilter } from "../common/ops.js";
|
||||
import { flexLayout, measureText } from "../common/render.js";
|
||||
import { statCardLocales, wakatimeCardLocales } from "../translations.js";
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ const normalizeCardWidth = ({ value, layout }) => {
|
||||
*/
|
||||
const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
let { languages = [] } = stats;
|
||||
let {
|
||||
const {
|
||||
hide_title = false,
|
||||
hide_border = false,
|
||||
card_width,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @ts-check
|
||||
|
||||
import { OWNER_AFFILIATIONS } from "./utils.js";
|
||||
import { OWNER_AFFILIATIONS } from "./render.js";
|
||||
|
||||
/**
|
||||
* @type {string} A general message to ask user to try again later.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
import toEmoji from "emoji-name-map";
|
||||
import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js";
|
||||
|
||||
/**
|
||||
* Returns boolean if value is either "true" or "false" else the value as it is.
|
||||
@@ -113,6 +114,49 @@ const dateDiff = (d1, d2) => {
|
||||
return Math.round(diff / (1000 * 60));
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse owner affiliations.
|
||||
*
|
||||
* @param {string[]} affiliations input affiliations to be parsed.
|
||||
* @returns {string[]} Parsed affiliations.
|
||||
*
|
||||
* @throws {CustomError} If affiliations contains invalid values.
|
||||
*/
|
||||
const parseOwnerAffiliations = (affiliations) => {
|
||||
// Set default value for ownerAffiliations.
|
||||
// NOTE: Done here since parseArray() will always return an empty array even nothing
|
||||
//was specified.
|
||||
affiliations =
|
||||
affiliations && affiliations.length > 0
|
||||
? affiliations.map((affiliation) => affiliation.toUpperCase())
|
||||
: ["OWNER"];
|
||||
|
||||
// Check if ownerAffiliations contains valid values.
|
||||
if (
|
||||
affiliations.some(
|
||||
(affiliation) => !OWNER_AFFILIATIONS.includes(affiliation),
|
||||
)
|
||||
) {
|
||||
throw new CustomError(
|
||||
"Invalid query parameter",
|
||||
CustomError.INVALID_AFFILIATION,
|
||||
);
|
||||
}
|
||||
return affiliations;
|
||||
};
|
||||
|
||||
const buildSearchFilter = (repos = [], owners = []) => {
|
||||
let repoFilter =
|
||||
Array.isArray(repos) && repos.length > 0
|
||||
? repos.map((r) => `repo:${r} `).join("")
|
||||
: "";
|
||||
let orgFilter =
|
||||
Array.isArray(owners) && owners.length > 0
|
||||
? owners.map((o) => `owner:${o} `).join("")
|
||||
: "";
|
||||
return repoFilter + orgFilter;
|
||||
};
|
||||
|
||||
export {
|
||||
parseBoolean,
|
||||
parseArray,
|
||||
@@ -121,4 +165,6 @@ export {
|
||||
chunkArray,
|
||||
parseEmojis,
|
||||
dateDiff,
|
||||
parseOwnerAffiliations,
|
||||
buildSearchFilter,
|
||||
};
|
||||
|
||||
@@ -117,26 +117,6 @@ const iconWithLabel = (icon, label, testid, iconSize) => {
|
||||
return flexLayout({ items: [iconSvg, text], gap: 20 }).join("");
|
||||
};
|
||||
|
||||
const buildSearchFilter = (repos = [], owners = []) => {
|
||||
let repoFilter =
|
||||
Array.isArray(repos) && repos.length > 0
|
||||
? repos.map((r) => `repo:${r} `).join("")
|
||||
: "";
|
||||
let orgFilter =
|
||||
Array.isArray(owners) && owners.length > 0
|
||||
? owners.map((o) => `owner:${o} `).join("")
|
||||
: "";
|
||||
return repoFilter + orgFilter;
|
||||
};
|
||||
|
||||
// Script parameters.
|
||||
const ERROR_CARD_LENGTH = 576.5;
|
||||
|
||||
const UPSTREAM_API_ERRORS = [
|
||||
TRY_AGAIN_LATER,
|
||||
SECONDARY_ERROR_MESSAGES.MAX_RETRY,
|
||||
];
|
||||
|
||||
/**
|
||||
* Renders error message on the card.
|
||||
*
|
||||
@@ -242,36 +222,13 @@ const measureText = (str, fontSize = 10) => {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse owner affiliations.
|
||||
*
|
||||
* @param {string[]} affiliations input affiliations to be parsed.
|
||||
* @returns {string[]} Parsed affiliations.
|
||||
*
|
||||
* @throws {CustomError} If affiliations contains invalid values.
|
||||
*/
|
||||
const parseOwnerAffiliations = (affiliations) => {
|
||||
// Set default value for ownerAffiliations.
|
||||
// NOTE: Done here since parseArray() will always return an empty array even nothing
|
||||
//was specified.
|
||||
affiliations =
|
||||
affiliations && affiliations.length > 0
|
||||
? affiliations.map((affiliation) => affiliation.toUpperCase())
|
||||
: ["OWNER"];
|
||||
// Script parameters.
|
||||
const ERROR_CARD_LENGTH = 576.5;
|
||||
|
||||
// Check if ownerAffiliations contains valid values.
|
||||
if (
|
||||
affiliations.some(
|
||||
(affiliation) => !OWNER_AFFILIATIONS.includes(affiliation),
|
||||
)
|
||||
) {
|
||||
throw new CustomError(
|
||||
"Invalid query parameter",
|
||||
CustomError.INVALID_AFFILIATION,
|
||||
);
|
||||
}
|
||||
return affiliations;
|
||||
};
|
||||
const UPSTREAM_API_ERRORS = [
|
||||
TRY_AGAIN_LATER,
|
||||
SECONDARY_ERROR_MESSAGES.MAX_RETRY,
|
||||
];
|
||||
|
||||
export {
|
||||
ERROR_CARD_LENGTH,
|
||||
@@ -279,9 +236,7 @@ export {
|
||||
createLanguageNode,
|
||||
createProgressNode,
|
||||
iconWithLabel,
|
||||
buildSearchFilter,
|
||||
flexLayout,
|
||||
OWNER_AFFILIATIONS,
|
||||
measureText,
|
||||
parseOwnerAffiliations,
|
||||
};
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
import { SECONDARY_ERROR_MESSAGES, TRY_AGAIN_LATER } from "./error.js";
|
||||
import { getCardColors } from "./color.js";
|
||||
|
||||
const OWNER_AFFILIATIONS = ["OWNER", "COLLABORATOR", "ORGANIZATION_MEMBER"];
|
||||
|
||||
/**
|
||||
* Auto layout utility, allows us to layout things vertically or horizontally with
|
||||
* proper gaping.
|
||||
*
|
||||
* @param {object} props Function properties.
|
||||
* @param {string[]} props.items Array of items to layout.
|
||||
* @param {number} props.gap Gap between items.
|
||||
* @param {"column" | "row"=} props.direction Direction to layout items.
|
||||
* @param {number[]=} props.sizes Array of sizes for each item.
|
||||
* @returns {string[]} Array of items with proper layout.
|
||||
*/
|
||||
const flexLayout = ({ items, gap, direction, sizes = [] }) => {
|
||||
let lastSize = 0;
|
||||
// filter() for filtering out empty strings
|
||||
return items.filter(Boolean).map((item, i) => {
|
||||
const size = sizes[i] || 0;
|
||||
let transform = `translate(${lastSize}, 0)`;
|
||||
if (direction === "column") {
|
||||
transform = `translate(0, ${lastSize})`;
|
||||
}
|
||||
lastSize += size + gap;
|
||||
return `<g transform="${transform}">${item}</g>`;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a node to display the primary programming language of the repository/gist.
|
||||
*
|
||||
* @param {string} langName Language name.
|
||||
* @param {string} langColor Language color.
|
||||
* @returns {string} Language display SVG object.
|
||||
*/
|
||||
const createLanguageNode = (langName, langColor) => {
|
||||
return `
|
||||
<g data-testid="primary-lang">
|
||||
<circle data-testid="lang-color" cx="0" cy="-5" r="6" fill="${langColor}" />
|
||||
<text data-testid="lang-name" class="gray" x="15">${langName}</text>
|
||||
</g>
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an icon with label to display repository/gist stats like forks, stars, etc.
|
||||
*
|
||||
* @param {string} icon The icon to display.
|
||||
* @param {number|string} label The label to display.
|
||||
* @param {string} testid The testid to assign to the label.
|
||||
* @param {number} iconSize The size of the icon.
|
||||
* @returns {string} Icon with label SVG object.
|
||||
*/
|
||||
const iconWithLabel = (icon, label, testid, iconSize) => {
|
||||
if (typeof label === "number" && label <= 0) {
|
||||
return "";
|
||||
}
|
||||
const iconSvg = `
|
||||
<svg
|
||||
class="icon"
|
||||
y="-12"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
width="${iconSize}"
|
||||
height="${iconSize}"
|
||||
>
|
||||
${icon}
|
||||
</svg>
|
||||
`;
|
||||
const text = `<text data-testid="${testid}" class="gray">${label}</text>`;
|
||||
return flexLayout({ items: [iconSvg, text], gap: 20 }).join("");
|
||||
};
|
||||
|
||||
const buildSearchFilter = (repos = [], owners = []) => {
|
||||
let repoFilter =
|
||||
Array.isArray(repos) && repos.length > 0
|
||||
? repos.map((r) => `repo:${r} `).join("")
|
||||
: "";
|
||||
let orgFilter =
|
||||
Array.isArray(owners) && owners.length > 0
|
||||
? owners.map((o) => `owner:${o} `).join("")
|
||||
: "";
|
||||
return repoFilter + orgFilter;
|
||||
};
|
||||
|
||||
// Script parameters.
|
||||
const ERROR_CARD_LENGTH = 576.5;
|
||||
|
||||
/**
|
||||
* Encode string as HTML.
|
||||
*
|
||||
* @see https://stackoverflow.com/a/48073476/10629172
|
||||
*
|
||||
* @param {string} str String to encode.
|
||||
* @returns {string} Encoded string.
|
||||
*/
|
||||
const encodeHTML = (str) => {
|
||||
return str
|
||||
.replace(/[\u00A0-\u9999<>&](?!#)/gim, (i) => {
|
||||
return "&#" + i.charCodeAt(0) + ";";
|
||||
})
|
||||
.replace(/\u0008/gim, "");
|
||||
};
|
||||
|
||||
const UPSTREAM_API_ERRORS = [
|
||||
TRY_AGAIN_LATER,
|
||||
SECONDARY_ERROR_MESSAGES.MAX_RETRY,
|
||||
];
|
||||
|
||||
/**
|
||||
* Renders error message on the card.
|
||||
*
|
||||
* @param {object} args Function arguments.
|
||||
* @param {string} args.message Main error message.
|
||||
* @param {string} [args.secondaryMessage=""] The secondary error message.
|
||||
* @param {object} [args.renderOptions={}] Render options.
|
||||
* @param {string=} args.renderOptions.title_color Card title color.
|
||||
* @param {string=} args.renderOptions.text_color Card text color.
|
||||
* @param {string=} args.renderOptions.bg_color Card background color.
|
||||
* @param {string=} args.renderOptions.border_color Card border color.
|
||||
* @param {Parameters<typeof getCardColors>[0]["theme"]=} args.renderOptions.theme Card theme.
|
||||
* @param {boolean=} args.renderOptions.show_repo_link Whether to show repo link or not.
|
||||
* @returns {string} The SVG markup.
|
||||
*/
|
||||
const renderError = ({
|
||||
message,
|
||||
secondaryMessage = "",
|
||||
renderOptions = {},
|
||||
}) => {
|
||||
const {
|
||||
title_color,
|
||||
text_color,
|
||||
bg_color,
|
||||
border_color,
|
||||
theme = "default",
|
||||
show_repo_link = true,
|
||||
} = renderOptions;
|
||||
|
||||
// returns theme based colors with proper overrides and defaults
|
||||
const { titleColor, textColor, bgColor, borderColor } = getCardColors({
|
||||
title_color,
|
||||
text_color,
|
||||
icon_color: "",
|
||||
bg_color,
|
||||
border_color,
|
||||
ring_color: "",
|
||||
theme,
|
||||
});
|
||||
|
||||
return `
|
||||
<svg width="${ERROR_CARD_LENGTH}" height="120" viewBox="0 0 ${ERROR_CARD_LENGTH} 120" fill="${bgColor}" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.text { font: 600 16px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${titleColor} }
|
||||
.small { font: 600 12px 'Segoe UI', Ubuntu, Sans-Serif; fill: ${textColor} }
|
||||
.gray { fill: #858585 }
|
||||
</style>
|
||||
<rect x="0.5" y="0.5" width="${
|
||||
ERROR_CARD_LENGTH - 1
|
||||
}" height="99%" rx="4.5" fill="${bgColor}" stroke="${borderColor}"/>
|
||||
<text x="25" y="45" class="text">Something went wrong!${
|
||||
UPSTREAM_API_ERRORS.includes(secondaryMessage) || !show_repo_link
|
||||
? ""
|
||||
: " file an issue at https://tiny.one/readme-stats"
|
||||
}</text>
|
||||
<text data-testid="message" x="25" y="55" class="text small">
|
||||
<tspan x="25" dy="18">${encodeHTML(message)}</tspan>
|
||||
<tspan x="25" dy="18" class="gray">${secondaryMessage}</tspan>
|
||||
</text>
|
||||
</svg>
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve text length.
|
||||
*
|
||||
* @see https://stackoverflow.com/a/48172630/10629172
|
||||
* @param {string} str String to measure.
|
||||
* @param {number} fontSize Font size.
|
||||
* @returns {number} Text length.
|
||||
*/
|
||||
const measureText = (str, fontSize = 10) => {
|
||||
// prettier-ignore
|
||||
const widths = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0.2796875, 0.2765625,
|
||||
0.3546875, 0.5546875, 0.5546875, 0.8890625, 0.665625, 0.190625,
|
||||
0.3328125, 0.3328125, 0.3890625, 0.5828125, 0.2765625, 0.3328125,
|
||||
0.2765625, 0.3015625, 0.5546875, 0.5546875, 0.5546875, 0.5546875,
|
||||
0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875, 0.5546875,
|
||||
0.2765625, 0.2765625, 0.584375, 0.5828125, 0.584375, 0.5546875,
|
||||
1.0140625, 0.665625, 0.665625, 0.721875, 0.721875, 0.665625,
|
||||
0.609375, 0.7765625, 0.721875, 0.2765625, 0.5, 0.665625,
|
||||
0.5546875, 0.8328125, 0.721875, 0.7765625, 0.665625, 0.7765625,
|
||||
0.721875, 0.665625, 0.609375, 0.721875, 0.665625, 0.94375,
|
||||
0.665625, 0.665625, 0.609375, 0.2765625, 0.3546875, 0.2765625,
|
||||
0.4765625, 0.5546875, 0.3328125, 0.5546875, 0.5546875, 0.5,
|
||||
0.5546875, 0.5546875, 0.2765625, 0.5546875, 0.5546875, 0.221875,
|
||||
0.240625, 0.5, 0.221875, 0.8328125, 0.5546875, 0.5546875,
|
||||
0.5546875, 0.5546875, 0.3328125, 0.5, 0.2765625, 0.5546875,
|
||||
0.5, 0.721875, 0.5, 0.5, 0.5, 0.3546875, 0.259375, 0.353125, 0.5890625,
|
||||
];
|
||||
|
||||
const avg = 0.5279276315789471;
|
||||
return (
|
||||
str
|
||||
.split("")
|
||||
.map((c) =>
|
||||
c.charCodeAt(0) < widths.length ? widths[c.charCodeAt(0)] : avg,
|
||||
)
|
||||
.reduce((cur, acc) => acc + cur) * fontSize
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse owner affiliations.
|
||||
*
|
||||
* @param {string[]} affiliations input affiliations to be parsed.
|
||||
* @returns {string[]} Parsed affiliations.
|
||||
*
|
||||
* @throws {CustomError} If affiliations contains invalid values.
|
||||
*/
|
||||
const parseOwnerAffiliations = (affiliations) => {
|
||||
// Set default value for ownerAffiliations.
|
||||
// NOTE: Done here since parseArray() will always return an empty array even nothing
|
||||
//was specified.
|
||||
affiliations =
|
||||
affiliations && affiliations.length > 0
|
||||
? affiliations.map((affiliation) => affiliation.toUpperCase())
|
||||
: ["OWNER"];
|
||||
|
||||
// Check if ownerAffiliations contains valid values.
|
||||
if (
|
||||
affiliations.some(
|
||||
(affiliation) => !OWNER_AFFILIATIONS.includes(affiliation),
|
||||
)
|
||||
) {
|
||||
throw new CustomError(
|
||||
"Invalid query parameter",
|
||||
CustomError.INVALID_AFFILIATION,
|
||||
);
|
||||
}
|
||||
return affiliations;
|
||||
};
|
||||
|
||||
export {
|
||||
ERROR_CARD_LENGTH,
|
||||
renderError,
|
||||
createLanguageNode,
|
||||
iconWithLabel,
|
||||
encodeHTML,
|
||||
buildSearchFilter,
|
||||
flexLayout,
|
||||
OWNER_AFFILIATIONS,
|
||||
measureText,
|
||||
parseOwnerAffiliations,
|
||||
};
|
||||
@@ -8,7 +8,7 @@ import { retryer } from "../common/retryer.js";
|
||||
import {
|
||||
buildSearchFilter,
|
||||
parseOwnerAffiliations,
|
||||
} from "../common/utils.js";
|
||||
} from "../common/ops.js";
|
||||
import { logger } from "../common/log.js";
|
||||
import { excludeRepositories } from "../common/envs.js";
|
||||
import { CustomError, MissingParamError } from "../common/error.js";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-check
|
||||
|
||||
import { retryer } from "../common/retryer.js";
|
||||
import { parseOwnerAffiliations } from "../common/utils.js";
|
||||
import { parseOwnerAffiliations } from "../common/ops.js";
|
||||
import { logger } from "../common/log.js";
|
||||
import { excludeRepositories } from "../common/envs.js";
|
||||
import { CustomError, MissingParamError } from "../common/error.js";
|
||||
|
||||
Reference in New Issue
Block a user