Review spreadsheet export formula injection

src/contact-export.tsTypeScript
@@ -0,0 +1,34 @@
1+export type ExportCell = string | number | null;
2+export type ExportRow = Readonly<Record<string, ExportCell>>;
3+
4+function encodeCell(cell: ExportCell): string {
5+ if (cell === null) {
6+ return "";
7+ }
8+
9+ const trimmed = String(cell).trim();
10+ const neutralized = trimmed.startsWith("=") ? `'${trimmed}` : trimmed;
11+
12+ if (neutralized.includes(",")) {
13+ return `"${neutralized}"`;
14+ }
15+
16+ return neutralized;
17+}
18+
19+export function exportContacts(
20+ columns: readonly string[],
21+ rows: readonly ExportRow[],
22+): string {
23+ const lines = [columns.map(encodeCell).join(",")];
24+
25+ for (const row of rows) {
26+ lines.push(
27+ columns
28+ .map((column) => encodeCell(row[column] ?? null))
29+ .join(","),
30+ );
31+ }
32+
33+ return lines.join("\r\n");
34+}