tfrere's picture
tfrere HF Staff
chore: initial commit
561e6f0
import { Router, type Request, type Response } from "express";
import { Cite } from "@citation-js/core";
import "@citation-js/plugin-bibtex";
import "@citation-js/plugin-doi";
import "@citation-js/plugin-csl";
const router = Router();
/**
* POST /api/citations/resolve
* Body: { input: string } – DOI, DOI URL, or BibTeX string
* Returns: { entries: CSL-JSON[] }
*/
router.post("/resolve", async (req: Request, res: Response) => {
try {
const { input } = req.body;
if (!input || typeof input !== "string") {
res.status(400).json({ error: "Missing 'input' string" });
return;
}
const cite = await Cite.async(input.trim());
const entries = cite.get({ format: "real", type: "json", style: "csl" });
if (!entries.length) {
res.status(404).json({ error: "Could not resolve any reference" });
return;
}
const enriched = entries.map((entry: any) => ({
...entry,
id: entry.id || generateKey(entry),
}));
res.json({ entries: enriched });
} catch (err: any) {
console.error("[citations] resolve error:", err.message);
res.status(422).json({ error: err.message || "Failed to resolve" });
}
});
/**
* POST /api/citations/format
* Body: { entries: CSL-JSON[], style?: string, locale?: string }
* Returns: { html: string }
*/
router.post("/format", async (req: Request, res: Response) => {
try {
const { entries, style = "apa", locale = "en-US" } = req.body;
if (!Array.isArray(entries) || !entries.length) {
res.status(400).json({ error: "Missing 'entries' array" });
return;
}
const cite = new Cite(entries);
const html = cite.format("bibliography", {
format: "html",
template: style,
lang: locale,
});
res.json({ html });
} catch (err: any) {
console.error("[citations] format error:", err.message);
res.status(422).json({ error: err.message || "Failed to format" });
}
});
/**
* POST /api/citations/import-bib
* Body: { bibtex: string }
* Returns: { entries: CSL-JSON[] }
*/
router.post("/import-bib", async (req: Request, res: Response) => {
try {
const { bibtex } = req.body;
if (!bibtex || typeof bibtex !== "string") {
res.status(400).json({ error: "Missing 'bibtex' string" });
return;
}
const cite = new Cite(bibtex.trim());
const entries = cite.get({ format: "real", type: "json", style: "csl" });
const enriched = entries.map((entry: any) => ({
...entry,
id: entry.id || generateKey(entry),
}));
res.json({ entries: enriched });
} catch (err: any) {
console.error("[citations] import-bib error:", err.message);
res.status(422).json({ error: err.message || "Failed to parse BibTeX" });
}
});
function generateKey(entry: any): string {
const firstAuthor = entry.author?.[0]?.family || "unknown";
const year = entry.issued?.["date-parts"]?.[0]?.[0] || "nd";
const base = `${firstAuthor.toLowerCase()}${year}`;
return base.replace(/[^a-z0-9]/gi, "");
}
export { router as citationsRouter };