import { tryParseJson } from "@oh-my-pi/pi-utils";
import type { RenderResult, SpecialHandler } from "./types";
import { buildResult, htmlToBasicMarkdown, loadPage } from "./types";
interface CrossrefAuthor {
given?: string;
family?: string;
name?: string;
}
interface CrossrefDate {
"date-parts"?: number[][];
}
interface CrossrefMessage {
title?: string[];
author?: CrossrefAuthor[];
"container-title"?: string[];
"short-container-title"?: string[];
publisher?: string;
published?: CrossrefDate;
"published-print"?: CrossrefDate;
"published-online"?: CrossrefDate;
issued?: CrossrefDate;
created?: CrossrefDate;
DOI?: string;
abstract?: string;
type?: string;
}
interface CrossrefResponse {
message?: CrossrefMessage;
}
const DOI_HOSTS = new Set(["doi.org", "dx.doi.org", "www.doi.org"]);
function extractDoi(pathname: string): string | null {
const raw = pathname.replace(/^\/+/, "");
if (!raw) return null;
return decodeURIComponent(raw);
}
function formatAuthors(authors?: CrossrefAuthor[]): string | null {
if (!authors || authors.length === 0) return null;
const names = authors
.map(author => {
if (author.name) return author.name;
const parts = [author.given, author.family].filter(Boolean);
return parts.length > 0 ? parts.join(" ") : null;
})
.filter((name): name is string => Boolean(name));
if (names.length === 0) return null;
return names.join(", ");
}
function formatDate(date?: CrossrefDate): string | null {
const parts = date?.["date-parts"]?.[0];
if (!parts || parts.length === 0) return null;
const [year, month, day] = parts;
if (!year) return null;
const formatted = [
String(year),
month ? String(month).padStart(2, "0") : "",
day ? String(day).padStart(2, "0") : "",
].filter(Boolean);
return formatted.join("-");
}
async function formatAbstract(abstract?: string): Promise
"));
const markdown = await htmlToBasicMarkdown(normalized);
return markdown.trim().length > 0 ? markdown : null;
}
export const handleCrossref: SpecialHandler = async (
url: string,
timeout: number,
signal?: AbortSignal,
): Promise