export function escapeCsv(value: string | number | null | undefined): string { if (value === null || value === undefined) return '' let str = String(value) // Prevent CSV formula injection (Excel/LibreOffice execute cells starting with these chars) if (/^[=+\-@\t\r]/.test(str)) str = "'" + str if (str.includes(',') || str.includes('"') || str.includes('\n')) { return `"${str.replace(/"/g, '""')}"` } return str } export function rowsToCsv(headers: string[], rows: string[][]): string { const lines = [headers.map(escapeCsv).join(',')] for (const row of rows) lines.push(row.map(escapeCsv).join(',')) return lines.join('\r\n') }