import { readFile, mkdir, writeFile } from 'node:fs/promises' import { dirname } from 'node:path' const CACHE_PATH = '.import-cache/og.json' type Cache = Record export type OgMeta = { title?: string; description?: string; image?: string } async function load(): Promise { try { return JSON.parse(await readFile(CACHE_PATH, 'utf8')) } catch { return {} } } async function save(c: Cache) { await mkdir(dirname(CACHE_PATH), { recursive: true }) await writeFile(CACHE_PATH, JSON.stringify(c, null, 2)) } function pick(html: string, prop: string): string | undefined { const re = new RegExp( `]+(?:property|name)=["']${prop}["'][^>]+content=["']([^"']+)["']`, 'i', ) return html.match(re)?.[1] } export async function fetchOg(url: string, cache: Cache): Promise { if (cache[url]) return cache[url] try { const res = await fetch(url, { headers: { 'user-agent': 'arter-blog-importer/1.0' } }) if (!res.ok) throw new Error(`HTTP ${res.status}`) const html = await res.text() const meta: OgMeta = { title: pick(html, 'og:title') ?? pick(html, 'twitter:title'), description: pick(html, 'og:description') ?? pick(html, 'twitter:description'), image: pick(html, 'og:image') ?? pick(html, 'twitter:image'), } cache[url] = meta return meta } catch (err) { console.warn(`[og] failed for ${url}: ${(err as Error).message}`) cache[url] = {} return {} } } export async function loadOgCache() { return load() } export async function persistOgCache(c: Cache) { await save(c) }