//Imports import ejs from "ejs" import util from "util" import * as utils from "./utils.mjs" //Setup export default async function metrics({login, q}, {graphql, rest, plugins, conf, die = false, verify = false, convert = null}, {Plugins, Templates}) { //Compute rendering try { //Debug login = login.replace(/[\n\r]/g, "") console.debug(`metrics/compute/${login} > start`) console.debug(util.inspect(q, {depth:Infinity, maxStringLength:256})) //Load template const template = q.template || conf.settings.templates.default if ((!(template in Templates)) || (!(template in conf.templates)) || ((conf.settings.templates.enabled.length) && (!conf.settings.templates.enabled.includes(template)))) throw new Error("unsupported template") const {image, style, fonts, views, partials} = conf.templates[template] const computer = Templates[template].default || Templates[template] convert = convert ?? conf.metadata.templates[template].formats[0] ?? null console.debug(`metrics/compute/${login} > output format set to ${convert}`) //Initialization const pending = [] const {queries} = conf const data = {q, animated:true, large:false, base:{}, config:{}, errors:[], plugins:{}, computed:{}} const imports = { plugins:Plugins, templates:Templates, metadata:conf.metadata, ...utils, ...utils.formatters({timeZone:q["config.timezone"]}), ...(/markdown/.test(convert) ? { imgb64(url, options) { return options?.force ? utils.imgb64(...arguments) : url }, } : null), } const experimental = new Set(decodeURIComponent(q["experimental.features"] ?? "").split(" ").map(x => x.trim().toLocaleLowerCase()).filter(x => x)) if (conf.settings["debug.headless"]) imports.puppeteer.headless = false //Metrics insights if (convert === "insights") return metrics.insights.output({login, imports, conf}, {graphql, rest, Plugins, Templates}) //Partial parts { data.partials = new Set([ ...decodeURIComponent(q["config.order"] ?? "").split(",").map(x => x.trim().toLocaleLowerCase()).filter(partial => partials.includes(partial)), ...partials, ]) console.debug(`metrics/compute/${login} > content order : ${[...data.partials]}`) } //Executing base plugin and compute metrics console.debug(`metrics/compute/${login} > compute`) await Plugins.base({login, q, data, rest, graphql, plugins, queries, pending, imports}, conf) await computer({login, q}, {conf, data, rest, graphql, plugins, queries, account:data.account, convert, template}, {pending, imports}) const promised = await Promise.all(pending) //Check plugins errors const errors = [...promised.filter(({result = null}) => result?.error), ...data.errors] if (errors.length) { console.warn(`metrics/compute/${login} > ${errors.length} errors !`) if (die) throw new Error("An error occured during rendering, dying") else console.warn(util.inspect(errors, {depth:Infinity, maxStringLength:256})) } //JSON output if (convert === "json") { console.debug(`metrics/compute/${login} > json output`) const cache = new WeakSet() const rendered = JSON.parse(JSON.stringify(data, (key, value) => { if ((value instanceof Set)||(Array.isArray(value))) return [...value] if (value instanceof Map) return Object.fromEntries(value) if ((typeof value === "object")&&(value)) { if (cache.has(value)) return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, cache.has(v) ? "[Circular]" : v])) cache.add(value) } return value })) return {rendered, mime:"application/json"} } //Markdown output if (/markdown/.test(convert)) { //Retrieving template source console.debug(`metrics/compute/${login} > markdown render`) let source = image try { let template = `${q.markdown}`.replace(/\n/g, "") if (!/^https:/.test(template)) { const {data:{default_branch:branch, full_name:repo}} = await rest.repos.get({owner:login, repo:q.repo || login}) console.debug(`metrics/compute/${login} > on ${repo} with default branch ${branch}`) template = `https://raw.githubusercontent.com/${repo}/${branch}/${template}` } console.debug(`metrics/compute/${login} > fetching ${template}`) ;({data:source} = await imports.axios.get(template, {headers:{Accept:"text/plain"}})) } catch (error) { console.debug(error) } //Embed method const _q = q const embed = async (name, q = {}) => { //Check arguments console.debug(`metrics/compute/${login}/embed > ${name} >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`) if ((!name) || (typeof q !== "object") || (q === null)) { if (die) throw new Error("An error occured during embed rendering, dying") return "
⚠️ Failed to execute embed function: invalid arguments
" } console.debug(`metrics/compute/${login} > embed called with`) console.debug(q) let {base} = q q = {..._q, ...Object.fromEntries(Object.keys(Plugins).map(key => [key, false])), ...Object.fromEntries(conf.settings.plugins.base.parts.map(part => [`base.${part}`, false])), template:"classic", ...q} //Translate action syntax to web syntax let parts = [] if (base === true) q = {...q, ...Object.fromEntries(Object.entries(_q).filter(([key]) => /^base[.]?/.test(key)))} if (typeof base === "string") parts = base.split(",").map(x => x.trim()) if (Array.isArray(base)) parts = base for (const part of parts) q[`base.${part}`] = true if (convert === "markdown-pdf") { q["config.animations"] = false q.config_animations = false } q = Object.fromEntries([...Object.entries(q).map(([key, value]) => [key.replace(/^plugin_/, "").replace(/_/g, "."), value]), ["base", false]]) //Compute rendering const {rendered} = await metrics({login, q}, {...arguments[1], convert:["svg", "png", "jpeg"].includes(q["config.output"]) ? q["config.output"] : null}, arguments[2]) console.debug(`metrics/compute/${login}/embed > ${name} > success >>>>>>>>>>>>>>>>>>>>>>`) return `