recker 1.0.35 → 1.0.36-next.4f24ba8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ import type { ReckerResponse } from '../types/index.js';
2
+ export declare const CSV_MEDIA_TYPES: readonly ["text/csv", "application/csv", "text/x-csv", "application/x-csv"];
3
+ export declare const CSV_SUFFIX = "+csv";
4
+ export declare function isCsvContentType(contentType: string | null): boolean;
5
+ export interface CsvParseOptions {
6
+ headers?: boolean;
7
+ columns?: string[];
8
+ delimiter?: string;
9
+ quote?: string;
10
+ skipEmptyLines?: boolean;
11
+ trim?: boolean;
12
+ parseNumbers?: boolean;
13
+ parseBooleans?: boolean;
14
+ comment?: string;
15
+ maxRows?: number;
16
+ maxColumns?: number;
17
+ }
18
+ export interface CsvSerializeOptions {
19
+ headers?: boolean;
20
+ columns?: string[];
21
+ delimiter?: string;
22
+ quote?: string;
23
+ alwaysQuote?: boolean;
24
+ lineEnding?: string;
25
+ nullValue?: string;
26
+ }
27
+ export declare function parseCsv<T = Record<string, string>>(csv: string, options: CsvParseOptions & {
28
+ headers?: true;
29
+ }): T[];
30
+ export declare function parseCsv(csv: string, options: CsvParseOptions & {
31
+ headers: false;
32
+ }): string[][];
33
+ export declare function parseCsv<T = Record<string, string>>(csv: string, options?: CsvParseOptions): T[] | string[][];
34
+ export declare class CsvError extends Error {
35
+ row?: number;
36
+ constructor(message: string, row?: number);
37
+ }
38
+ export declare function serializeCsv(data: Record<string, any>[] | any[][], options?: CsvSerializeOptions): string;
39
+ export declare function csvResponse<T = Record<string, string>>(promise: Promise<ReckerResponse>, options?: CsvParseOptions): Promise<T[]>;
40
+ export declare function parseCsvTyped<T = Record<string, any>>(csv: string, options?: Omit<CsvParseOptions, 'parseNumbers' | 'parseBooleans'>): T[];
41
+ export declare function parseCsvStream<T = Record<string, string>>(stream: ReadableStream<Uint8Array> | null, options?: CsvParseOptions): AsyncGenerator<T>;
42
+ export { parseCsv as csvParse, serializeCsv as csvSerialize };
@@ -0,0 +1,386 @@
1
+ export const CSV_MEDIA_TYPES = [
2
+ 'text/csv',
3
+ 'application/csv',
4
+ 'text/x-csv',
5
+ 'application/x-csv',
6
+ ];
7
+ export const CSV_SUFFIX = '+csv';
8
+ export function isCsvContentType(contentType) {
9
+ if (!contentType)
10
+ return false;
11
+ const ct = contentType.toLowerCase().split(';')[0].trim();
12
+ return CSV_MEDIA_TYPES.includes(ct) || ct.endsWith(CSV_SUFFIX);
13
+ }
14
+ export function parseCsv(csv, options = {}) {
15
+ const opts = {
16
+ headers: options.headers ?? true,
17
+ columns: options.columns,
18
+ delimiter: options.delimiter ?? ',',
19
+ quote: options.quote ?? '"',
20
+ skipEmptyLines: options.skipEmptyLines ?? true,
21
+ trim: options.trim ?? false,
22
+ parseNumbers: options.parseNumbers ?? false,
23
+ parseBooleans: options.parseBooleans ?? false,
24
+ comment: options.comment,
25
+ maxRows: options.maxRows ?? 100000,
26
+ maxColumns: options.maxColumns ?? 1000,
27
+ };
28
+ if (!csv || !csv.trim()) {
29
+ return [];
30
+ }
31
+ const normalized = csv.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
32
+ const rows = [];
33
+ let currentRow = [];
34
+ let currentField = '';
35
+ let inQuotes = false;
36
+ let rowCount = 0;
37
+ for (let i = 0; i < normalized.length; i++) {
38
+ const char = normalized[i];
39
+ const nextChar = normalized[i + 1];
40
+ if (inQuotes) {
41
+ if (char === opts.quote) {
42
+ if (nextChar === opts.quote) {
43
+ currentField += opts.quote;
44
+ i++;
45
+ }
46
+ else {
47
+ inQuotes = false;
48
+ }
49
+ }
50
+ else {
51
+ currentField += char;
52
+ }
53
+ }
54
+ else {
55
+ if (char === opts.quote && currentField === '') {
56
+ inQuotes = true;
57
+ }
58
+ else if (char === opts.delimiter) {
59
+ currentRow.push(processField(currentField, opts));
60
+ currentField = '';
61
+ if (currentRow.length > opts.maxColumns) {
62
+ throw new CsvError(`Maximum columns (${opts.maxColumns}) exceeded`, rowCount);
63
+ }
64
+ }
65
+ else if (char === '\n') {
66
+ currentRow.push(processField(currentField, opts));
67
+ currentField = '';
68
+ if (opts.comment && currentRow.length === 1 && currentRow[0].startsWith(opts.comment)) {
69
+ currentRow = [];
70
+ continue;
71
+ }
72
+ if (opts.skipEmptyLines && currentRow.length === 1 && currentRow[0] === '') {
73
+ currentRow = [];
74
+ continue;
75
+ }
76
+ rows.push(currentRow);
77
+ currentRow = [];
78
+ rowCount++;
79
+ if (rowCount > opts.maxRows) {
80
+ throw new CsvError(`Maximum rows (${opts.maxRows}) exceeded`, rowCount);
81
+ }
82
+ }
83
+ else {
84
+ currentField += char;
85
+ }
86
+ }
87
+ }
88
+ if (currentField || currentRow.length > 0) {
89
+ currentRow.push(processField(currentField, opts));
90
+ if (!(opts.comment && currentRow.length === 1 && currentRow[0].startsWith(opts.comment))) {
91
+ if (!(opts.skipEmptyLines && currentRow.length === 1 && currentRow[0] === '')) {
92
+ rows.push(currentRow);
93
+ }
94
+ }
95
+ }
96
+ if (!opts.headers && !opts.columns) {
97
+ return rows;
98
+ }
99
+ let headers;
100
+ let dataRows;
101
+ if (opts.columns) {
102
+ headers = opts.columns;
103
+ dataRows = opts.headers ? rows.slice(1) : rows;
104
+ }
105
+ else {
106
+ if (rows.length === 0) {
107
+ return [];
108
+ }
109
+ headers = rows[0];
110
+ dataRows = rows.slice(1);
111
+ }
112
+ return dataRows.map((row) => {
113
+ const obj = {};
114
+ for (let i = 0; i < headers.length; i++) {
115
+ obj[headers[i]] = row[i] ?? '';
116
+ }
117
+ return obj;
118
+ });
119
+ }
120
+ function processField(field, opts) {
121
+ let value = opts.trim ? field.trim() : field;
122
+ if (opts.parseNumbers) {
123
+ const num = Number(value);
124
+ if (!isNaN(num) && value !== '') {
125
+ return num;
126
+ }
127
+ }
128
+ if (opts.parseBooleans) {
129
+ const lower = value.toLowerCase();
130
+ if (lower === 'true')
131
+ return true;
132
+ if (lower === 'false')
133
+ return false;
134
+ }
135
+ return value;
136
+ }
137
+ export class CsvError extends Error {
138
+ row;
139
+ constructor(message, row) {
140
+ super(row !== undefined ? `${message} at row ${row + 1}` : message);
141
+ this.name = 'CsvError';
142
+ this.row = row;
143
+ }
144
+ }
145
+ export function serializeCsv(data, options = {}) {
146
+ const opts = {
147
+ headers: options.headers ?? true,
148
+ columns: options.columns,
149
+ delimiter: options.delimiter ?? ',',
150
+ quote: options.quote ?? '"',
151
+ alwaysQuote: options.alwaysQuote ?? false,
152
+ lineEnding: options.lineEnding ?? '\r\n',
153
+ nullValue: options.nullValue ?? '',
154
+ };
155
+ if (!data || data.length === 0) {
156
+ return '';
157
+ }
158
+ const lines = [];
159
+ const isArrayOfArrays = Array.isArray(data[0]);
160
+ if (isArrayOfArrays) {
161
+ for (const row of data) {
162
+ lines.push(row.map((field) => quoteField(field, opts)).join(opts.delimiter));
163
+ }
164
+ }
165
+ else {
166
+ const objects = data;
167
+ let columns;
168
+ if (opts.columns) {
169
+ columns = opts.columns;
170
+ }
171
+ else {
172
+ const keySet = new Set();
173
+ for (const obj of objects) {
174
+ for (const key of Object.keys(obj)) {
175
+ keySet.add(key);
176
+ }
177
+ }
178
+ columns = Array.from(keySet);
179
+ }
180
+ if (opts.headers) {
181
+ lines.push(columns.map((col) => quoteField(col, opts)).join(opts.delimiter));
182
+ }
183
+ for (const obj of objects) {
184
+ const row = columns.map((col) => {
185
+ const value = obj[col];
186
+ return quoteField(value, opts);
187
+ });
188
+ lines.push(row.join(opts.delimiter));
189
+ }
190
+ }
191
+ return lines.join(opts.lineEnding);
192
+ }
193
+ function quoteField(value, opts) {
194
+ if (value === null || value === undefined) {
195
+ return opts.nullValue;
196
+ }
197
+ const str = String(value);
198
+ const needsQuoting = opts.alwaysQuote ||
199
+ str.includes(opts.quote) ||
200
+ str.includes(opts.delimiter) ||
201
+ str.includes('\n') ||
202
+ str.includes('\r');
203
+ if (needsQuoting) {
204
+ const escaped = str.replace(new RegExp(opts.quote, 'g'), opts.quote + opts.quote);
205
+ return opts.quote + escaped + opts.quote;
206
+ }
207
+ return str;
208
+ }
209
+ export async function csvResponse(promise, options) {
210
+ const response = await promise;
211
+ const text = await response.text();
212
+ return parseCsv(text, options);
213
+ }
214
+ export function parseCsvTyped(csv, options = {}) {
215
+ const rows = parseCsv(csv, { ...options, headers: true });
216
+ return rows.map((row) => {
217
+ const typed = {};
218
+ for (const [key, value] of Object.entries(row)) {
219
+ typed[key] = inferType(value);
220
+ }
221
+ return typed;
222
+ });
223
+ }
224
+ function inferType(value) {
225
+ if (value === '') {
226
+ return null;
227
+ }
228
+ const lower = value.toLowerCase();
229
+ if (lower === 'true')
230
+ return true;
231
+ if (lower === 'false')
232
+ return false;
233
+ if (/^-?\d+$/.test(value)) {
234
+ const num = parseInt(value, 10);
235
+ if (Number.isSafeInteger(num))
236
+ return num;
237
+ }
238
+ if (/^-?\d*\.?\d+([eE][-+]?\d+)?$/.test(value)) {
239
+ return parseFloat(value);
240
+ }
241
+ if (/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/.test(value)) {
242
+ const date = new Date(value);
243
+ if (!isNaN(date.getTime())) {
244
+ return date;
245
+ }
246
+ }
247
+ return value;
248
+ }
249
+ export async function* parseCsvStream(stream, options = {}) {
250
+ if (!stream)
251
+ return;
252
+ const opts = {
253
+ headers: options.headers ?? true,
254
+ columns: options.columns,
255
+ delimiter: options.delimiter ?? ',',
256
+ quote: options.quote ?? '"',
257
+ skipEmptyLines: options.skipEmptyLines ?? true,
258
+ trim: options.trim ?? false,
259
+ parseNumbers: options.parseNumbers ?? false,
260
+ parseBooleans: options.parseBooleans ?? false,
261
+ comment: options.comment,
262
+ };
263
+ const decoder = new TextDecoder();
264
+ const reader = stream.getReader();
265
+ let buffer = '';
266
+ let headers = opts.columns || null;
267
+ let isFirstRow = !opts.columns;
268
+ try {
269
+ while (true) {
270
+ const { done, value } = await reader.read();
271
+ if (done) {
272
+ if (buffer.trim()) {
273
+ const row = parseRow(buffer, opts);
274
+ if (row) {
275
+ if (isFirstRow && opts.headers) {
276
+ headers = row;
277
+ isFirstRow = false;
278
+ }
279
+ else if (headers) {
280
+ yield rowToObject(row, headers);
281
+ }
282
+ else {
283
+ yield row;
284
+ }
285
+ }
286
+ }
287
+ break;
288
+ }
289
+ buffer += decoder.decode(value, { stream: true });
290
+ let newlineIndex;
291
+ while ((newlineIndex = findCompleteRow(buffer, opts.quote)) !== -1) {
292
+ const line = buffer.slice(0, newlineIndex);
293
+ buffer = buffer.slice(newlineIndex + 1);
294
+ if (buffer.startsWith('\r')) {
295
+ buffer = buffer.slice(1);
296
+ }
297
+ const row = parseRow(line, opts);
298
+ if (!row)
299
+ continue;
300
+ if (isFirstRow && opts.headers) {
301
+ headers = row;
302
+ isFirstRow = false;
303
+ continue;
304
+ }
305
+ if (headers) {
306
+ yield rowToObject(row, headers);
307
+ }
308
+ else {
309
+ yield row;
310
+ }
311
+ }
312
+ }
313
+ }
314
+ finally {
315
+ reader.releaseLock();
316
+ }
317
+ }
318
+ function findCompleteRow(buffer, quote) {
319
+ let inQuotes = false;
320
+ for (let i = 0; i < buffer.length; i++) {
321
+ const char = buffer[i];
322
+ if (char === quote) {
323
+ if (inQuotes && buffer[i + 1] === quote) {
324
+ i++;
325
+ }
326
+ else {
327
+ inQuotes = !inQuotes;
328
+ }
329
+ }
330
+ else if ((char === '\n' || char === '\r') && !inQuotes) {
331
+ return i;
332
+ }
333
+ }
334
+ return -1;
335
+ }
336
+ function parseRow(line, opts) {
337
+ if (opts.comment && line.startsWith(opts.comment)) {
338
+ return null;
339
+ }
340
+ if (opts.skipEmptyLines && !line.trim()) {
341
+ return null;
342
+ }
343
+ const fields = [];
344
+ let currentField = '';
345
+ let inQuotes = false;
346
+ for (let i = 0; i < line.length; i++) {
347
+ const char = line[i];
348
+ const nextChar = line[i + 1];
349
+ if (inQuotes) {
350
+ if (char === opts.quote) {
351
+ if (nextChar === opts.quote) {
352
+ currentField += opts.quote;
353
+ i++;
354
+ }
355
+ else {
356
+ inQuotes = false;
357
+ }
358
+ }
359
+ else {
360
+ currentField += char;
361
+ }
362
+ }
363
+ else {
364
+ if (char === opts.quote && currentField === '') {
365
+ inQuotes = true;
366
+ }
367
+ else if (char === opts.delimiter) {
368
+ fields.push(processField(currentField, opts));
369
+ currentField = '';
370
+ }
371
+ else {
372
+ currentField += char;
373
+ }
374
+ }
375
+ }
376
+ fields.push(processField(currentField, opts));
377
+ return fields;
378
+ }
379
+ function rowToObject(row, headers) {
380
+ const obj = {};
381
+ for (let i = 0; i < headers.length; i++) {
382
+ obj[headers[i]] = row[i] ?? '';
383
+ }
384
+ return obj;
385
+ }
386
+ export { parseCsv as csvParse, serializeCsv as csvSerialize };
@@ -0,0 +1,28 @@
1
+ import type { ReckerResponse } from '../types/index.js';
2
+ export declare const YAML_MEDIA_TYPES: readonly ["application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"];
3
+ export declare const YAML_SUFFIX = "+yaml";
4
+ export declare function isYamlContentType(contentType: string | null): boolean;
5
+ export interface YamlParseOptions {
6
+ allowDuplicateKeys?: boolean;
7
+ parseDates?: boolean;
8
+ customTags?: Record<string, (value: string) => any>;
9
+ maxDepth?: number;
10
+ maxKeys?: number;
11
+ }
12
+ export interface YamlSerializeOptions {
13
+ indent?: number;
14
+ lineWidth?: number;
15
+ forceQuotes?: boolean;
16
+ flowStyle?: boolean;
17
+ sortKeys?: boolean;
18
+ skipUndefined?: boolean;
19
+ documentMarkers?: boolean;
20
+ }
21
+ export declare function parseYaml<T = any>(yaml: string, options?: YamlParseOptions): T;
22
+ export declare class YamlError extends Error {
23
+ line?: number;
24
+ constructor(message: string, line?: number);
25
+ }
26
+ export declare function serializeYaml(data: any, options?: YamlSerializeOptions): string;
27
+ export declare function yamlResponse<T = any>(promise: Promise<ReckerResponse>, options?: YamlParseOptions): Promise<T>;
28
+ export { parseYaml as yamlParse, serializeYaml as yamlSerialize };