// Shared minimal CSV parsing helpers -- extracted from // src/lib/expected-assets.ts (2026-08-21) when src/lib/assets.ts needed // the same line-splitting logic for AssetItem import (2026-08-23). Handles // quoted fields (incl. embedded commas/escaped double-quotes) without a // dependency, which is enough for this app's actual import files (simple // flat tables, not arbitrary user-supplied CSV). /** * Splits one CSV line into fields. A quote only opens a quoted field when * it's the very first character of that field -- a stray/legitimate quote * mid-value (e.g. a 24" monitor size) is kept as a literal character * instead of wrongly starting quoted-content mode. */ export function splitCsvLine(line: string): string[] { const fields: string[] = []; let current = ""; let inQuotes = false; for (let i = 0; i < line.length; i++) { const char = line[i]; if (inQuotes) { if (char === '"') { if (line[i + 1] === '"') { current += '"'; i++; } else { inQuotes = false; } } else { current += char; } } else if (char === '"' && current === "") { inQuotes = true; } else if (char === ",") { fields.push(current); current = ""; } else { current += char; } } fields.push(current); return fields.map((f) => f.trim()); } /** Splits raw CSV text into non-blank lines (handles \r\n, \r, \n). */ export function splitCsvLines(text: string): string[] { return text.split(/\r\n|\r|\n/).filter((line) => line.trim() !== ""); } export function normalizeHeader(header: string): string { return header.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " "); } /** Quotes a single CSV field for output if it contains a comma, quote, or * newline -- doubling any embedded quotes, per RFC4180. */ export function csvField(value: string): string { if (/[",\n]/.test(value)) { return `"${value.replace(/"/g, '""')}"`; } return value; }