import puppeteer, { type Browser } from 'puppeteer' /** * HTML → PDF via puppeteer. The browser is a lazy module-level singleton: * launch costs ~1s, so it starts on first render and is reused after. * Guarding with a promise (not the Browser) makes concurrent first calls * share one launch instead of racing two Chromes. */ let browserPromise: Promise | null = null function getBrowser(): Promise { browserPromise ??= puppeteer.launch() return browserPromise } export async function renderPdf(html: string): Promise { const page = await (await getBrowser()).newPage() try { await page.setContent(html, { waitUntil: 'load' }) const bytes = await page.pdf({ format: 'A4', printBackground: true }) return Buffer.from(bytes) } finally { await page.close() } } /** For tests/scripts that must let the process exit; the server never calls it. */ export async function closePdfBrowser(): Promise { if (browserPromise === null) return const b = await browserPromise browserPromise = null await b.close() }