type Cell = string | number | null | undefined /** * RFC 4180 CSV with a BOM so Excel opens UTF-8 correctly. Cells that a spreadsheet would treat as a * formula are prefixed with an apostrophe (CSV injection). */ export function toCsv(header: string[], rows: Cell[][]): string { const cell = (value: Cell) => { let text = value === null || value === undefined ? '' : String(value) if (/^[=+\-@\t\r]/.test(text)) text = `'${text}` return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text } return '' + [header, ...rows].map((row) => row.map(cell).join(',')).join('\r\n') + '\r\n' }