@mitre/hdf-utilities 3.0.0 → 3.1.0-rc.1

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.
package/LICENSE.md ADDED
@@ -0,0 +1,55 @@
1
+ # License
2
+
3
+ Copyright © 2025 The MITRE Corporation.
4
+
5
+ Approved for Public Release; Distribution Unlimited. Case Number 18-3678.
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
8
+ not use this file except in compliance with the License. You may obtain a
9
+ copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
+ License for the specific language governing permissions and limitations
17
+ under the License.
18
+
19
+ ## Redistribution Terms
20
+
21
+ Redistribution and use in source and binary forms, with or without
22
+ modification, are permitted provided that the following conditions are
23
+ met:
24
+
25
+ - Redistributions of source code must retain the above copyright/digital
26
+ rights legend, this list of conditions and the following Notice.
27
+ - Redistributions in binary form must reproduce the above
28
+ copyright/digital rights legend, this list of conditions and the
29
+ following Notice in the documentation and/or other materials provided
30
+ with the distribution.
31
+ - Neither the name of The MITRE Corporation nor the names of its contributors
32
+ may be used to endorse or promote products derived from this software
33
+ without specific prior written permission.
34
+
35
+ ## Notice
36
+
37
+ The MITRE Corporation grants permission to reproduce, distribute, modify, and
38
+ otherwise use this software to the extent permitted by the licensed terms
39
+ provided in the LICENSE file included with this project.
40
+
41
+ This software was produced by The MITRE Corporation for the U.S. Government
42
+ under contract. As such the U.S. Government has certain use and data
43
+ rights in this software. No use other than those granted to the U.S.
44
+ Government, or to those acting on behalf of the U.S. Government, under
45
+ these contract arrangements is authorized without the express written
46
+ permission of The MITRE Corporation.
47
+
48
+ Some files in this codebase were generated by generative AI, under the
49
+ direction and review of The MITRE Corporation employees, for the purpose of
50
+ development efficiency. All AI-generated code functionality was validated
51
+ by standard quality and assurance testing.
52
+
53
+ For further information, please contact The MITRE Corporation,
54
+ Contracts Management Office, 7515 Colshire Drive, McLean, VA 22102-7539,
55
+ (703) 983-6000.
@@ -1,29 +1,25 @@
1
- /**
2
- * CSV parsing and generation utilities using d3-dsv
3
- *
4
- * SECURITY WARNING: CSV files opened in Excel/LibreOffice may execute formulas.
5
- * Use sanitization options when exporting data that may contain user-controlled content.
6
- */
7
- export { extractColumn as extractCsvColumn, findRows as findCsvRows, } from '../object/index.js';
1
+ import { extractColumn, findRows } from "../object/index.js";
2
+
3
+ //#region src/csv/index.d.ts
8
4
  /**
9
5
  * Options for parsing CSV
10
6
  */
11
- export interface CsvParseOptions {
12
- header?: boolean;
13
- skipEmptyLines?: boolean;
14
- transformHeader?: (header: string, index: number) => string;
15
- dynamicTyping?: boolean;
16
- delimiter?: string;
17
- maxSize?: number;
7
+ interface CsvParseOptions {
8
+ header?: boolean;
9
+ skipEmptyLines?: boolean;
10
+ transformHeader?: (header: string, index: number) => string;
11
+ dynamicTyping?: boolean;
12
+ delimiter?: string;
13
+ maxSize?: number;
18
14
  }
19
15
  /**
20
16
  * Options for building CSV
21
17
  */
22
- export interface CsvBuildOptions {
23
- header?: boolean;
24
- newline?: string;
25
- skipEmptyLines?: boolean;
26
- delimiter?: string;
18
+ interface CsvBuildOptions {
19
+ header?: boolean;
20
+ newline?: string;
21
+ skipEmptyLines?: boolean;
22
+ delimiter?: string;
27
23
  }
28
24
  /**
29
25
  * Parse CSV string into array of objects with headers as keys
@@ -32,7 +28,7 @@ export interface CsvBuildOptions {
32
28
  * @returns Array of objects representing rows
33
29
  * @throws Error if parsing fails
34
30
  */
35
- export declare function parseCsv<T = Record<string, unknown>>(csv: string, options?: Partial<CsvParseOptions>): T[];
31
+ declare function parseCsv<T = Record<string, unknown>>(csv: string, options?: Partial<CsvParseOptions>): T[];
36
32
  /**
37
33
  * Parse CSV string into array of arrays without treating first row as headers
38
34
  * @param csv - CSV string to parse
@@ -40,7 +36,7 @@ export declare function parseCsv<T = Record<string, unknown>>(csv: string, optio
40
36
  * @returns Array of arrays representing rows
41
37
  * @throws Error if parsing fails
42
38
  */
43
- export declare function parseCsvArray(csv: string, options?: Partial<CsvParseOptions>): string[][];
39
+ declare function parseCsvArray(csv: string, options?: Partial<CsvParseOptions>): string[][];
44
40
  /**
45
41
  * Build CSV string from array of objects
46
42
  *
@@ -51,8 +47,8 @@ export declare function parseCsvArray(csv: string, options?: Partial<CsvParseOpt
51
47
  * @param options - Optional build configuration and sanitize flag
52
48
  * @returns CSV string
53
49
  */
54
- export declare function buildCsv<T = Record<string, unknown>>(data: T[], options?: Partial<CsvBuildOptions> & {
55
- sanitize?: boolean;
50
+ declare function buildCsv<T = Record<string, unknown>>(data: T[], options?: Partial<CsvBuildOptions> & {
51
+ sanitize?: boolean;
56
52
  }): string;
57
53
  /**
58
54
  * Build CSV string from array of arrays
@@ -64,15 +60,15 @@ export declare function buildCsv<T = Record<string, unknown>>(data: T[], options
64
60
  * @param options - Optional build configuration and sanitize flag
65
61
  * @returns CSV string
66
62
  */
67
- export declare function buildCsvArray(data: string[][], options?: Partial<CsvBuildOptions> & {
68
- sanitize?: boolean;
63
+ declare function buildCsvArray(data: string[][], options?: Partial<CsvBuildOptions> & {
64
+ sanitize?: boolean;
69
65
  }): string;
70
66
  /**
71
67
  * Validate CSV format
72
68
  * @param csv - CSV string to validate
73
69
  * @returns True if valid CSV, false otherwise
74
70
  */
75
- export declare function isValidCsv(csv: string): boolean;
71
+ declare function isValidCsv(csv: string): boolean;
76
72
  /**
77
73
  * Sanitize a single CSV value to prevent formula injection
78
74
  *
@@ -82,19 +78,21 @@ export declare function isValidCsv(csv: string): boolean;
82
78
  * @param value - Value to sanitize
83
79
  * @returns Sanitized value
84
80
  */
85
- export declare function sanitizeCsvValue(value: unknown): string;
81
+ declare function sanitizeCsvValue(value: unknown): string;
86
82
  /**
87
83
  * Sanitize all values in an array to prevent formula injection
88
84
  *
89
85
  * @param values - Array of values to sanitize
90
86
  * @returns Array with sanitized values
91
87
  */
92
- export declare function sanitizeCsvArray(values: unknown[]): string[];
88
+ declare function sanitizeCsvArray(values: unknown[]): string[];
93
89
  /**
94
90
  * Sanitize all values in an object to prevent formula injection
95
91
  *
96
92
  * @param obj - Object with values to sanitize
97
93
  * @returns New object with sanitized values
98
94
  */
99
- export declare function sanitizeCsvObject<T extends Record<string, unknown>>(obj: T): Record<string, string>;
95
+ declare function sanitizeCsvObject<T extends Record<string, unknown>>(obj: T): Record<string, string>;
96
+ //#endregion
97
+ export { CsvBuildOptions, CsvParseOptions, buildCsv, buildCsvArray, extractColumn as extractCsvColumn, findRows as findCsvRows, isValidCsv, parseCsv, parseCsvArray, sanitizeCsvArray, sanitizeCsvObject, sanitizeCsvValue };
100
98
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/csv/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EACL,aAAa,IAAI,gBAAgB,EACjC,QAAQ,IAAI,WAAW,GACxB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GACjC,CAAC,EAAE,CA2CL;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GACjC,MAAM,EAAE,EAAE,CAoBZ;AAED;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,IAAI,EAAE,CAAC,EAAE,EACT,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1D,MAAM,CA8BR;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EAAE,EAAE,EAChB,OAAO,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC1D,MAAM,CAmBR;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAe/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAOvD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,GAAG,EAAE,CAAC,GACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/csv/index.ts"],"mappings":";;;;;;UAiBiB,eAAA;EACf,MAAA;EACA,cAAA;EACA,eAAA,IAAmB,MAAA,UAAgB,KAAA;EACnC,aAAA;EACA,SAAA;EACA,OAAA;AAAA;;;;UAMe,eAAA;EACf,MAAA;EACA,OAAA;EACA,cAAA;EACA,SAAA;AAAA;AAwEF;;;;;;;AAAA,iBAAgB,QAAA,KAAa,MAAA,kBAAA,CAC3B,GAAA,UACA,OAAA,GAAU,OAAA,CAAQ,eAAA,IACjB,CAAA;;;;;;;;iBAoDa,aAAA,CACd,GAAA,UACA,OAAA,GAAU,OAAA,CAAQ,eAAA;;;AAFpB;;;;;;;;iBAmCgB,QAAA,KAAa,MAAA,kBAAA,CAC3B,IAAA,EAAM,CAAA,IACN,OAAA,GAAU,OAAA,CAAQ,eAAA;EAAqB,QAAA;AAAA;;;;;;;;;;;iBA2CzB,aAAA,CACd,IAAA,cACA,OAAA,GAAU,OAAA,CAAQ,eAAA;EAAqB,QAAA;AAAA;;;;;;iBA2BzB,UAAA,CAAW,GAAA;;;;;;;;;;iBA0BX,gBAAA,CAAiB,KAAA;AA1BjC;;;;;AA0BA;AA1BA,iBAyCgB,gBAAA,CAAiB,MAAA;;;;AAAjC;;;iBAUgB,iBAAA,WAA4B,MAAA,kBAAA,CAC1C,GAAA,EAAK,CAAA,GACJ,MAAA"}
package/dist/csv/index.js CHANGED
@@ -1,251 +1,212 @@
1
+ import { extractColumn, findRows } from "../object/index.js";
2
+ import { dsvFormat } from "d3-dsv";
3
+ //#region src/csv/index.ts
1
4
  /**
2
- * CSV parsing and generation utilities using d3-dsv
3
- *
4
- * SECURITY WARNING: CSV files opened in Excel/LibreOffice may execute formulas.
5
- * Use sanitization options when exporting data that may contain user-controlled content.
6
- */
7
- import { dsvFormat } from 'd3-dsv';
8
- export { extractColumn as extractCsvColumn, findRows as findCsvRows, } from '../object/index.js';
5
+ * CSV parsing and generation utilities using d3-dsv
6
+ *
7
+ * SECURITY WARNING: CSV files opened in Excel/LibreOffice may execute formulas.
8
+ * Use sanitization options when exporting data that may contain user-controlled content.
9
+ */
9
10
  /**
10
- * Default options for parsing CSV with headers
11
- */
11
+ * Default options for parsing CSV with headers
12
+ */
12
13
  const DEFAULT_PARSE_OPTIONS = {
13
- header: true,
14
- skipEmptyLines: true,
15
- transformHeader: undefined,
16
- dynamicTyping: false,
14
+ header: true,
15
+ skipEmptyLines: true,
16
+ transformHeader: void 0,
17
+ dynamicTyping: false
17
18
  };
18
19
  /**
19
- * Default options for parsing CSV without headers
20
- */
20
+ * Default options for parsing CSV without headers
21
+ */
21
22
  const DEFAULT_PARSE_ARRAY_OPTIONS = {
22
- header: false,
23
- skipEmptyLines: true,
24
- dynamicTyping: false,
23
+ header: false,
24
+ skipEmptyLines: true,
25
+ dynamicTyping: false
25
26
  };
26
27
  /**
27
- * Default options for building CSV
28
- */
28
+ * Default options for building CSV
29
+ */
29
30
  const DEFAULT_BUILD_OPTIONS = {
30
- header: true,
31
- newline: '\n',
32
- skipEmptyLines: true,
31
+ header: true,
32
+ newline: "\n",
33
+ skipEmptyLines: true
33
34
  };
34
35
  /**
35
- * Validate CSV quoting — throws if unclosed quotes are found
36
- */
36
+ * Validate CSV quoting — throws if unclosed quotes are found
37
+ */
37
38
  function validateQuoting(csv) {
38
- let inQuotes = false;
39
- let row = 0;
40
- for (let i = 0; i < csv.length; i++) {
41
- if (csv[i] === '"') {
42
- if (inQuotes && i + 1 < csv.length && csv[i + 1] === '"') {
43
- i++; // skip escaped quote ""
44
- }
45
- else {
46
- inQuotes = !inQuotes;
47
- }
48
- }
49
- else if (csv[i] === '\n' && !inQuotes) {
50
- row++;
51
- }
52
- }
53
- if (inQuotes) {
54
- throw new Error(`CSV parsing failed: Row ${row}: Unclosed quote`);
55
- }
39
+ let inQuotes = false;
40
+ let row = 0;
41
+ for (let i = 0; i < csv.length; i++) if (csv[i] === "\"") if (inQuotes && i + 1 < csv.length && csv[i + 1] === "\"") i++;
42
+ else inQuotes = !inQuotes;
43
+ else if (csv[i] === "\n" && !inQuotes) row++;
44
+ if (inQuotes) throw new Error(`CSV parsing failed: Row ${row}: Unclosed quote`);
56
45
  }
57
46
  /**
58
- * Convert string value to number or boolean when appropriate
59
- */
47
+ * Convert string value to number or boolean when appropriate
48
+ */
60
49
  function dynamicTypeConvert(value) {
61
- if (value === '')
62
- return value;
63
- if (value === 'true')
64
- return true;
65
- if (value === 'false')
66
- return false;
67
- const num = Number(value);
68
- if (!isNaN(num) && value.trim() !== '')
69
- return num;
70
- return value;
50
+ if (value === "") return value;
51
+ if (value === "true") return true;
52
+ if (value === "false") return false;
53
+ const num = Number(value);
54
+ if (!isNaN(num) && value.trim() !== "") return num;
55
+ return value;
71
56
  }
72
57
  /**
73
- * Parse CSV string into array of objects with headers as keys
74
- * @param csv - CSV string to parse
75
- * @param options - Optional parse configuration
76
- * @returns Array of objects representing rows
77
- * @throws Error if parsing fails
78
- */
79
- export function parseCsv(csv, options) {
80
- if (options?.maxSize !== undefined && csv.length > options.maxSize) {
81
- throw new Error(`Input exceeds maximum allowed size: ${csv.length} bytes exceeds limit of ${options.maxSize} bytes`);
82
- }
83
- const opts = { ...DEFAULT_PARSE_OPTIONS, ...options };
84
- const trimmed = csv.trim();
85
- validateQuoting(trimmed);
86
- const dsv = dsvFormat(opts.delimiter ?? ',');
87
- const parsed = dsv.parse(trimmed);
88
- const { columns } = parsed;
89
- // Pre-compute transformed headers
90
- const headers = opts.transformHeader
91
- ? columns.map((col, i) => opts.transformHeader(col, i))
92
- : columns;
93
- let rows = parsed.map((row) => {
94
- const obj = {};
95
- columns.forEach((col, i) => {
96
- let value = row[col] ?? '';
97
- if (opts.dynamicTyping && typeof value === 'string') {
98
- value = dynamicTypeConvert(value);
99
- }
100
- const key = headers[i];
101
- if (key !== undefined) {
102
- obj[key] = value;
103
- }
104
- });
105
- return obj;
106
- });
107
- if (opts.skipEmptyLines) {
108
- rows = rows.filter((row) => Object.values(row).some((v) => v !== '' && v !== null && v !== undefined));
109
- }
110
- return rows;
58
+ * Parse CSV string into array of objects with headers as keys
59
+ * @param csv - CSV string to parse
60
+ * @param options - Optional parse configuration
61
+ * @returns Array of objects representing rows
62
+ * @throws Error if parsing fails
63
+ */
64
+ function parseCsv(csv, options) {
65
+ if (options?.maxSize !== void 0 && csv.length > options.maxSize) throw new Error(`Input exceeds maximum allowed size: ${csv.length} bytes exceeds limit of ${options.maxSize} bytes`);
66
+ const opts = {
67
+ ...DEFAULT_PARSE_OPTIONS,
68
+ ...options
69
+ };
70
+ const trimmed = csv.trim();
71
+ validateQuoting(trimmed);
72
+ const parsed = dsvFormat(opts.delimiter ?? ",").parse(trimmed);
73
+ const { columns } = parsed;
74
+ const headers = opts.transformHeader ? columns.map((col, i) => opts.transformHeader(col, i)) : columns;
75
+ let rows = parsed.map((row) => {
76
+ const obj = {};
77
+ columns.forEach((col, i) => {
78
+ let value = row[col] ?? "";
79
+ if (opts.dynamicTyping && typeof value === "string") value = dynamicTypeConvert(value);
80
+ const key = headers[i];
81
+ if (key !== void 0) obj[key] = value;
82
+ });
83
+ return obj;
84
+ });
85
+ if (opts.skipEmptyLines) rows = rows.filter((row) => Object.values(row).some((v) => v !== "" && v !== null && v !== void 0));
86
+ return rows;
111
87
  }
112
88
  /**
113
- * Parse CSV string into array of arrays without treating first row as headers
114
- * @param csv - CSV string to parse
115
- * @param options - Optional parse configuration
116
- * @returns Array of arrays representing rows
117
- * @throws Error if parsing fails
118
- */
119
- export function parseCsvArray(csv, options) {
120
- if (options?.maxSize !== undefined && csv.length > options.maxSize) {
121
- throw new Error(`Input exceeds maximum allowed size: ${csv.length} bytes exceeds limit of ${options.maxSize} bytes`);
122
- }
123
- const opts = { ...DEFAULT_PARSE_ARRAY_OPTIONS, ...options };
124
- const trimmed = csv.trim();
125
- validateQuoting(trimmed);
126
- const dsv = dsvFormat(opts.delimiter ?? ',');
127
- let rows = dsv.parseRows(trimmed);
128
- if (opts.skipEmptyLines) {
129
- rows = rows.filter((row) => !(row.length === 1 && row[0] === ''));
130
- }
131
- return rows;
89
+ * Parse CSV string into array of arrays without treating first row as headers
90
+ * @param csv - CSV string to parse
91
+ * @param options - Optional parse configuration
92
+ * @returns Array of arrays representing rows
93
+ * @throws Error if parsing fails
94
+ */
95
+ function parseCsvArray(csv, options) {
96
+ if (options?.maxSize !== void 0 && csv.length > options.maxSize) throw new Error(`Input exceeds maximum allowed size: ${csv.length} bytes exceeds limit of ${options.maxSize} bytes`);
97
+ const opts = {
98
+ ...DEFAULT_PARSE_ARRAY_OPTIONS,
99
+ ...options
100
+ };
101
+ const trimmed = csv.trim();
102
+ validateQuoting(trimmed);
103
+ let rows = dsvFormat(opts.delimiter ?? ",").parseRows(trimmed);
104
+ if (opts.skipEmptyLines) rows = rows.filter((row) => !(row.length === 1 && row[0] === ""));
105
+ return rows;
132
106
  }
133
107
  /**
134
- * Build CSV string from array of objects
135
- *
136
- * SECURITY: Set sanitize=true when data may contain user-controlled content
137
- * to prevent CSV formula injection attacks in Excel/LibreOffice.
138
- *
139
- * @param data - Array of objects to convert to CSV
140
- * @param options - Optional build configuration and sanitize flag
141
- * @returns CSV string
142
- */
143
- export function buildCsv(data, options) {
144
- const { sanitize, ...buildOpts } = options || {};
145
- const opts = { ...DEFAULT_BUILD_OPTIONS, ...buildOpts };
146
- if (data.length === 0)
147
- return '';
148
- let processedData = data;
149
- if (sanitize) {
150
- processedData = data.map((row) => sanitizeCsvObject(row));
151
- }
152
- const dsv = dsvFormat(opts.delimiter ?? ',');
153
- let result;
154
- if (opts.header === false) {
155
- const rows = processedData.map((row) => Object.values(row).map(String));
156
- result = dsv.formatRows(rows);
157
- }
158
- else {
159
- result = dsv.format(processedData);
160
- }
161
- if (opts.newline && opts.newline !== '\n') {
162
- result = result.replace(/\n/g, opts.newline);
163
- }
164
- return result;
108
+ * Build CSV string from array of objects
109
+ *
110
+ * SECURITY: Set sanitize=true when data may contain user-controlled content
111
+ * to prevent CSV formula injection attacks in Excel/LibreOffice.
112
+ *
113
+ * @param data - Array of objects to convert to CSV
114
+ * @param options - Optional build configuration and sanitize flag
115
+ * @returns CSV string
116
+ */
117
+ function buildCsv(data, options) {
118
+ const { sanitize, ...buildOpts } = options || {};
119
+ const opts = {
120
+ ...DEFAULT_BUILD_OPTIONS,
121
+ ...buildOpts
122
+ };
123
+ if (data.length === 0) return "";
124
+ let processedData = data;
125
+ if (sanitize) processedData = data.map((row) => sanitizeCsvObject(row));
126
+ const dsv = dsvFormat(opts.delimiter ?? ",");
127
+ let result;
128
+ if (opts.header === false) {
129
+ const rows = processedData.map((row) => Object.values(row).map(String));
130
+ result = dsv.formatRows(rows);
131
+ } else result = dsv.format(processedData);
132
+ if (opts.newline && opts.newline !== "\n") result = result.replace(/\n/g, opts.newline);
133
+ return result;
165
134
  }
166
135
  /**
167
- * Build CSV string from array of arrays
168
- *
169
- * SECURITY: Set sanitize=true when data may contain user-controlled content
170
- * to prevent CSV formula injection attacks in Excel/LibreOffice.
171
- *
172
- * @param data - Array of arrays to convert to CSV
173
- * @param options - Optional build configuration and sanitize flag
174
- * @returns CSV string
175
- */
176
- export function buildCsvArray(data, options) {
177
- const { sanitize, ...buildOpts } = options || {};
178
- const opts = { ...DEFAULT_BUILD_OPTIONS, header: false, ...buildOpts };
179
- if (data.length === 0)
180
- return '';
181
- let processedData = data;
182
- if (sanitize) {
183
- processedData = data.map((row) => sanitizeCsvArray(row));
184
- }
185
- const dsv = dsvFormat(opts.delimiter ?? ',');
186
- let result = dsv.formatRows(processedData);
187
- if (opts.newline && opts.newline !== '\n') {
188
- result = result.replace(/\n/g, opts.newline);
189
- }
190
- return result;
136
+ * Build CSV string from array of arrays
137
+ *
138
+ * SECURITY: Set sanitize=true when data may contain user-controlled content
139
+ * to prevent CSV formula injection attacks in Excel/LibreOffice.
140
+ *
141
+ * @param data - Array of arrays to convert to CSV
142
+ * @param options - Optional build configuration and sanitize flag
143
+ * @returns CSV string
144
+ */
145
+ function buildCsvArray(data, options) {
146
+ const { sanitize, ...buildOpts } = options || {};
147
+ const opts = {
148
+ ...DEFAULT_BUILD_OPTIONS,
149
+ header: false,
150
+ ...buildOpts
151
+ };
152
+ if (data.length === 0) return "";
153
+ let processedData = data;
154
+ if (sanitize) processedData = data.map((row) => sanitizeCsvArray(row));
155
+ let result = dsvFormat(opts.delimiter ?? ",").formatRows(processedData);
156
+ if (opts.newline && opts.newline !== "\n") result = result.replace(/\n/g, opts.newline);
157
+ return result;
191
158
  }
192
159
  /**
193
- * Validate CSV format
194
- * @param csv - CSV string to validate
195
- * @returns True if valid CSV, false otherwise
196
- */
197
- export function isValidCsv(csv) {
198
- if (!csv || csv.trim().length === 0)
199
- return false;
200
- const trimmed = csv.trim();
201
- // CSV must have at least one delimiter (comma) to be considered CSV structure
202
- if (!trimmed.includes(','))
203
- return false;
204
- try {
205
- validateQuoting(trimmed);
206
- }
207
- catch {
208
- return false;
209
- }
210
- return true;
160
+ * Validate CSV format
161
+ * @param csv - CSV string to validate
162
+ * @returns True if valid CSV, false otherwise
163
+ */
164
+ function isValidCsv(csv) {
165
+ if (!csv || csv.trim().length === 0) return false;
166
+ const trimmed = csv.trim();
167
+ if (!trimmed.includes(",")) return false;
168
+ try {
169
+ validateQuoting(trimmed);
170
+ } catch {
171
+ return false;
172
+ }
173
+ return true;
211
174
  }
212
175
  /**
213
- * Sanitize a single CSV value to prevent formula injection
214
- *
215
- * Prefixes values starting with =, +, -, @, |, or % with a single quote
216
- * to prevent Excel/LibreOffice from interpreting them as formulas.
217
- *
218
- * @param value - Value to sanitize
219
- * @returns Sanitized value
220
- */
221
- export function sanitizeCsvValue(value) {
222
- const str = String(value);
223
- // Check if string starts with formula trigger characters
224
- if (/^[=+\-@|%]/.test(str)) {
225
- return `'${str}`;
226
- }
227
- return str;
176
+ * Sanitize a single CSV value to prevent formula injection
177
+ *
178
+ * Prefixes values starting with =, +, -, @, |, or % with a single quote
179
+ * to prevent Excel/LibreOffice from interpreting them as formulas.
180
+ *
181
+ * @param value - Value to sanitize
182
+ * @returns Sanitized value
183
+ */
184
+ function sanitizeCsvValue(value) {
185
+ const str = String(value);
186
+ if (/^[=+\-@|%]/.test(str)) return `'${str}`;
187
+ return str;
228
188
  }
229
189
  /**
230
- * Sanitize all values in an array to prevent formula injection
231
- *
232
- * @param values - Array of values to sanitize
233
- * @returns Array with sanitized values
234
- */
235
- export function sanitizeCsvArray(values) {
236
- return values.map(sanitizeCsvValue);
190
+ * Sanitize all values in an array to prevent formula injection
191
+ *
192
+ * @param values - Array of values to sanitize
193
+ * @returns Array with sanitized values
194
+ */
195
+ function sanitizeCsvArray(values) {
196
+ return values.map(sanitizeCsvValue);
237
197
  }
238
198
  /**
239
- * Sanitize all values in an object to prevent formula injection
240
- *
241
- * @param obj - Object with values to sanitize
242
- * @returns New object with sanitized values
243
- */
244
- export function sanitizeCsvObject(obj) {
245
- const sanitized = {};
246
- for (const [key, value] of Object.entries(obj)) {
247
- sanitized[key] = sanitizeCsvValue(value);
248
- }
249
- return sanitized;
199
+ * Sanitize all values in an object to prevent formula injection
200
+ *
201
+ * @param obj - Object with values to sanitize
202
+ * @returns New object with sanitized values
203
+ */
204
+ function sanitizeCsvObject(obj) {
205
+ const sanitized = {};
206
+ for (const [key, value] of Object.entries(obj)) sanitized[key] = sanitizeCsvValue(value);
207
+ return sanitized;
250
208
  }
209
+ //#endregion
210
+ export { buildCsv, buildCsvArray, extractColumn as extractCsvColumn, findRows as findCsvRows, isValidCsv, parseCsv, parseCsvArray, sanitizeCsvArray, sanitizeCsvObject, sanitizeCsvValue };
211
+
251
212
  //# sourceMappingURL=index.js.map