export type CitablePaper = { identifier: string; slug: string; title: string; subtitle?: string | null; year?: number | null; authors: { givenName: string; familyName: string }[]; seriesName: string; institution: string; url: string; versionLabel?: string | null; doi?: string | null; ssrnId?: string | null; keywords?: string[]; abstract?: string[]; releasedOn?: string | null; releasedPrecision?: string | null; pages?: number | null; jel?: string[]; }; const esc = (s: string) => s.replace(/[{}]/g, "").replace(/&/g, "\\&").replace(/%/g, "\\%"); function monthOf(d?: string | null) { if (!d) return null; const m = Number(d.slice(5, 7)); return Number.isFinite(m) && m >= 1 && m <= 12 ? m : null; } const MONTHS = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"]; function fullTitle(p: CitablePaper) { return p.subtitle ? `${p.title}: ${p.subtitle}` : p.title; } function authorsApa(a: CitablePaper["authors"]) { const parts = a.map((x) => `${x.familyName}, ${x.givenName.split(/[\s-]+/).map((g) => g[0] + ".").join("-")}`); if (parts.length <= 1) return parts[0] ?? ""; if (parts.length === 2) return `${parts[0]}, & ${parts[1]}`; return parts.slice(0, -1).join(", ") + ", & " + parts[parts.length - 1]; } function authorsChicago(a: CitablePaper["authors"]) { const first = a[0] ? `${a[0].familyName}, ${a[0].givenName}` : ""; const rest = a.slice(1).map((x) => `${x.givenName} ${x.familyName}`); if (!rest.length) return first; if (rest.length === 1) return `${first}, and ${rest[0]}`; return `${first}, ${rest.slice(0, -1).join(", ")}, and ${rest[rest.length - 1]}`; } export function citationApa(p: CitablePaper) { const y = p.year ?? "n.d."; return `${authorsApa(p.authors)} (${y}). ${fullTitle(p)} (${p.seriesName} ${p.identifier}${p.versionLabel ? ", " + p.versionLabel : ""}). ${p.institution}. ${p.doi ? `https://doi.org/${p.doi}` : p.url}`; } export function citationChicago(p: CitablePaper) { const y = p.year ?? "n.d."; return `${authorsChicago(p.authors)}. ${y}. “${fullTitle(p)}.” ${p.seriesName} ${p.identifier}. ${p.institution}. ${p.doi ? `https://doi.org/${p.doi}` : p.url}.`; } export function citationKey(p: CitablePaper) { const fam = (p.authors[0]?.familyName ?? "anon").normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^A-Za-z]/g, "").toLowerCase(); return `${fam}${p.year ?? ""}${p.slug}`; } export function bibtex(p: CitablePaper) { const m = monthOf(p.releasedOn); const lines = [ `@techreport{${citationKey(p)},`, ` title = {${esc(fullTitle(p))}},`, ` author = {${p.authors.map((a) => `${esc(a.familyName)}, ${esc(a.givenName)}`).join(" and ")}},`, p.year ? ` year = {${p.year}},` : null, m && p.releasedPrecision !== "year" ? ` month = ${MONTHS[m - 1]},` : null, ` institution = {${esc(p.institution)}},`, ` type = {${esc(p.seriesName)}},`, ` number = {${p.identifier}},`, p.versionLabel ? ` note = {Version ${p.versionLabel}},` : null, p.doi ? ` doi = {${p.doi}},` : null, ` url = {${p.url}},`, p.keywords?.length ? ` keywords = {${esc(p.keywords.join(", "))}},` : null, p.abstract?.length ? ` abstract = {${esc(p.abstract.join(" "))}},` : null, `}`, ].filter(Boolean); return lines.join("\n"); } export function ris(p: CitablePaper) { const lines = ["TY - RPRT", `TI - ${fullTitle(p)}`, ...p.authors.map((a) => `AU - ${a.familyName}, ${a.givenName}`), p.year ? `PY - ${p.year}` : null, p.releasedOn ? `DA - ${p.releasedOn.slice(0, 4)}/${p.releasedPrecision === "year" ? "" : p.releasedOn.slice(5, 7)}/${p.releasedPrecision === "day" ? p.releasedOn.slice(8, 10) : ""}/` : null, `PB - ${p.institution}`, `T2 - ${p.seriesName}`, `M1 - ${p.identifier}`, p.versionLabel ? `ET - ${p.versionLabel}` : null, p.doi ? `DO - ${p.doi}` : null, `UR - ${p.url}`, ...(p.keywords ?? []).map((k) => `KW - ${k}`), p.abstract?.length ? `AB - ${p.abstract.join(" ")}` : null, "ER - "].filter(Boolean); return lines.join("\r\n"); }