@juspay/neurolink 9.94.2 → 9.94.4
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/CHANGELOG.md +12 -0
- package/dist/agent/directTools.js +3 -1
- package/dist/browser/neurolink.min.js +400 -397
- package/dist/cli/factories/commandFactory.d.ts +6 -0
- package/dist/cli/factories/commandFactory.js +36 -8
- package/dist/lib/agent/directTools.js +3 -1
- package/dist/lib/neurolink.js +5 -0
- package/dist/lib/rag/document/loaders.js +20 -4
- package/dist/lib/types/file.d.ts +43 -0
- package/dist/lib/types/generate.d.ts +3 -11
- package/dist/lib/types/rag.d.ts +7 -0
- package/dist/lib/types/stream.d.ts +2 -6
- package/dist/lib/utils/csvProcessor.d.ts +68 -10
- package/dist/lib/utils/csvProcessor.js +499 -135
- package/dist/lib/utils/errorHandling.d.ts +32 -0
- package/dist/lib/utils/errorHandling.js +86 -0
- package/dist/lib/utils/imageProcessor.d.ts +42 -4
- package/dist/lib/utils/imageProcessor.js +96 -19
- package/dist/lib/utils/messageBuilder.js +49 -11
- package/dist/lib/utils/multimodalOptionsBuilder.d.ts +1 -5
- package/dist/lib/utils/textEncoding.d.ts +25 -0
- package/dist/lib/utils/textEncoding.js +141 -0
- package/dist/neurolink.js +5 -0
- package/dist/rag/document/loaders.js +20 -4
- package/dist/types/file.d.ts +43 -0
- package/dist/types/generate.d.ts +3 -11
- package/dist/types/rag.d.ts +7 -0
- package/dist/types/stream.d.ts +2 -6
- package/dist/utils/csvProcessor.d.ts +68 -10
- package/dist/utils/csvProcessor.js +498 -134
- package/dist/utils/errorHandling.d.ts +32 -0
- package/dist/utils/errorHandling.js +86 -0
- package/dist/utils/imageProcessor.d.ts +42 -4
- package/dist/utils/imageProcessor.js +96 -19
- package/dist/utils/messageBuilder.js +49 -11
- package/dist/utils/multimodalOptionsBuilder.d.ts +1 -5
- package/dist/utils/textEncoding.d.ts +25 -0
- package/dist/utils/textEncoding.js +140 -0
- package/package.json +3 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Buffer → string decoding with encoding detection (#362).
|
|
3
|
+
*
|
|
4
|
+
* Order of resolution:
|
|
5
|
+
* 1. Explicit override (any iconv-lite label).
|
|
6
|
+
* 2. Deterministic BOM sniff (UTF-8 / UTF-16 LE / UTF-16 BE).
|
|
7
|
+
* 3. `chardet` statistical detection, mapped to an iconv-lite label.
|
|
8
|
+
* 4. Fallback to UTF-8 (preserves the historical hard-coded default).
|
|
9
|
+
*
|
|
10
|
+
* Decoding always goes through `iconv-lite`, and any residual BOM is stripped
|
|
11
|
+
* from the decoded string so it can't glue onto the first token.
|
|
12
|
+
*/
|
|
13
|
+
import chardet from "chardet";
|
|
14
|
+
import iconv from "iconv-lite";
|
|
15
|
+
import { logger } from "./logger.js";
|
|
16
|
+
/** Strip a leading BOM (U+FEFF) from an already-decoded string. */
|
|
17
|
+
export function stripBomString(text) {
|
|
18
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
19
|
+
}
|
|
20
|
+
/** Deterministic BOM detection from the raw bytes. Returns null when absent. */
|
|
21
|
+
function sniffBom(buffer) {
|
|
22
|
+
if (buffer.length >= 3 &&
|
|
23
|
+
buffer[0] === 0xef &&
|
|
24
|
+
buffer[1] === 0xbb &&
|
|
25
|
+
buffer[2] === 0xbf) {
|
|
26
|
+
return "utf-8";
|
|
27
|
+
}
|
|
28
|
+
if (buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xfe) {
|
|
29
|
+
return "utf-16le";
|
|
30
|
+
}
|
|
31
|
+
if (buffer.length >= 2 && buffer[0] === 0xfe && buffer[1] === 0xff) {
|
|
32
|
+
return "utf-16be";
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Map a chardet label to an iconv-lite-supported label. Returns null when the
|
|
38
|
+
* label is unknown/unsupported so the caller falls back to UTF-8.
|
|
39
|
+
*/
|
|
40
|
+
function toIconvLabel(detected) {
|
|
41
|
+
if (!detected) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const normalized = detected.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
45
|
+
const map = {
|
|
46
|
+
utf8: "utf-8",
|
|
47
|
+
utf16le: "utf-16le",
|
|
48
|
+
utf16be: "utf-16be",
|
|
49
|
+
ascii: "utf-8", // ASCII is a strict UTF-8 subset
|
|
50
|
+
windows1252: "windows-1252",
|
|
51
|
+
iso88591: "latin1",
|
|
52
|
+
latin1: "latin1",
|
|
53
|
+
iso88592: "iso-8859-2",
|
|
54
|
+
windows1251: "windows-1251",
|
|
55
|
+
koi8r: "koi8-r",
|
|
56
|
+
gb18030: "gb18030",
|
|
57
|
+
big5: "big5",
|
|
58
|
+
shiftjis: "shift_jis",
|
|
59
|
+
euckr: "euc-kr",
|
|
60
|
+
eucjp: "euc-jp",
|
|
61
|
+
};
|
|
62
|
+
const label = map[normalized] ?? detected;
|
|
63
|
+
return iconv.encodingExists(label) ? label : null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode a buffer to text, detecting the encoding unless overridden.
|
|
67
|
+
*
|
|
68
|
+
* @param isCompleteBuffer - Whether `buffer` is the entire source (default
|
|
69
|
+
* `true`). Pass `false` when `buffer` is only a peeked prefix (e.g. a
|
|
70
|
+
* streaming head-sniff) — the ASCII fast path still commits to "utf-8" for
|
|
71
|
+
* the peeked bytes (unchanged behavior), but reports a lower confidence
|
|
72
|
+
* since later bytes not yet seen could still be non-ASCII (#1199).
|
|
73
|
+
*/
|
|
74
|
+
export function decodeBuffer(buffer, override, isCompleteBuffer = true) {
|
|
75
|
+
// 1. Explicit override wins outright.
|
|
76
|
+
if (override && iconv.encodingExists(override)) {
|
|
77
|
+
return {
|
|
78
|
+
text: stripBomString(iconv.decode(buffer, override)),
|
|
79
|
+
encoding: override,
|
|
80
|
+
confidence: 100,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (override && !iconv.encodingExists(override)) {
|
|
84
|
+
logger.warn(`[textEncoding] Unsupported encoding override "${override}"; falling back to detection.`);
|
|
85
|
+
}
|
|
86
|
+
// 2. BOM is authoritative when present.
|
|
87
|
+
const bom = sniffBom(buffer);
|
|
88
|
+
if (bom) {
|
|
89
|
+
return {
|
|
90
|
+
text: stripBomString(iconv.decode(buffer, bom)),
|
|
91
|
+
encoding: bom,
|
|
92
|
+
confidence: 100,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// 2b. Pure-ASCII fast path — chardet often reports ASCII as ISO-8859-1, but
|
|
96
|
+
// ASCII is a strict UTF-8 subset and decodes identically, so report "utf-8"
|
|
97
|
+
// to preserve the historical default for plain-ASCII files.
|
|
98
|
+
let ascii = true;
|
|
99
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
100
|
+
if (buffer[i] >= 0x80) {
|
|
101
|
+
ascii = false;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (ascii) {
|
|
106
|
+
return {
|
|
107
|
+
text: stripBomString(iconv.decode(buffer, "utf-8")),
|
|
108
|
+
encoding: "utf-8",
|
|
109
|
+
// A partial peek being pure-ASCII only proves the peeked prefix is
|
|
110
|
+
// ASCII, not the whole file — a later chunk could still switch to a
|
|
111
|
+
// legacy encoding. Only report full certainty when `buffer` is known
|
|
112
|
+
// to be the complete source.
|
|
113
|
+
confidence: isCompleteBuffer ? 100 : 60,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// 3. Statistical detection.
|
|
117
|
+
let detected = null;
|
|
118
|
+
try {
|
|
119
|
+
detected = chardet.detect(buffer);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
logger.debug(`[textEncoding] chardet failed: ${String(error)}`);
|
|
123
|
+
}
|
|
124
|
+
const label = toIconvLabel(detected);
|
|
125
|
+
if (label) {
|
|
126
|
+
return {
|
|
127
|
+
text: stripBomString(iconv.decode(buffer, label)),
|
|
128
|
+
encoding: label,
|
|
129
|
+
// chardet gives a winner but not a numeric score via detect(); treat a
|
|
130
|
+
// confident non-UTF-8 hit as reasonably (not fully) certain.
|
|
131
|
+
confidence: label === "utf-8" ? 100 : 80,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// 4. Fallback: historical UTF-8 default.
|
|
135
|
+
return {
|
|
136
|
+
text: stripBomString(iconv.decode(buffer, "utf-8")),
|
|
137
|
+
encoding: "utf-8",
|
|
138
|
+
confidence: 0,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=textEncoding.js.map
|
package/dist/neurolink.js
CHANGED
|
@@ -3820,6 +3820,11 @@ Current user's request: ${currentInput}`;
|
|
|
3820
3820
|
evaluationDomain: options.evaluationDomain,
|
|
3821
3821
|
toolUsageContext: options.toolUsageContext,
|
|
3822
3822
|
input: options.input,
|
|
3823
|
+
// CSV processing options must survive the reconstruction into
|
|
3824
|
+
// TextGenerationOptions — the message builder reads them (encoding,
|
|
3825
|
+
// formatting, sanitization, parse timeout). Omitting them silently
|
|
3826
|
+
// dropped every csvOptions field (incl. the CLI --csv-* flags).
|
|
3827
|
+
csvOptions: options.csvOptions,
|
|
3823
3828
|
region: options.region,
|
|
3824
3829
|
tts: options.tts,
|
|
3825
3830
|
stt: options.stt,
|
|
@@ -27,7 +27,7 @@ import { existsSync } from "fs";
|
|
|
27
27
|
import { readFile } from "fs/promises";
|
|
28
28
|
import { basename, extname } from "path";
|
|
29
29
|
import { logger } from "../../utils/logger.js";
|
|
30
|
-
import { CSVProcessor, stripBom, stripMetadataLine, splitCsvLines, } from "../../utils/csvProcessor.js";
|
|
30
|
+
import { CSVProcessor, stripBom, stripMetadataLine, splitCsvLines, dedupeColumnNames, sanitizeColumnName, } from "../../utils/csvProcessor.js";
|
|
31
31
|
import { splitCSVFields } from "../../utils/csvUtils.js";
|
|
32
32
|
import { MDocument } from "./MDocument.js";
|
|
33
33
|
/**
|
|
@@ -117,7 +117,7 @@ export class JSONLoader extends TextLoader {
|
|
|
117
117
|
export class CSVLoader extends TextLoader {
|
|
118
118
|
async load(source, options) {
|
|
119
119
|
const content = await this.loadContent(source, options?.encoding);
|
|
120
|
-
const { delimiter, hasHeader = true, columns, outputFormat = "text", } = options || {};
|
|
120
|
+
const { delimiter, hasHeader = true, columns, outputFormat = "text", sanitizeColumnNames = false, columnNameCase = "snake_case", } = options || {};
|
|
121
121
|
// #361: an explicit delimiter always wins; otherwise detect it from the
|
|
122
122
|
// content + extension so a .tsv fed through RAG isn't collapsed into one
|
|
123
123
|
// comma-column (CSVLoader.canHandle already accepts .tsv).
|
|
@@ -133,16 +133,32 @@ export class CSVLoader extends TextLoader {
|
|
|
133
133
|
// escaping or quoted delimiters in the headerless-columns branch.
|
|
134
134
|
const { dataLines: strippedLines } = stripMetadataLine(splitCsvLines(stripBom(content)));
|
|
135
135
|
const lines = strippedLines.filter((line) => line.trim());
|
|
136
|
-
const
|
|
136
|
+
const rawHeaders = hasHeader
|
|
137
137
|
? splitCSVFields(lines[0] ?? "", effectiveDelimiter).map((h) => h.trim())
|
|
138
138
|
: columns ||
|
|
139
139
|
splitCSVFields(lines[0] ?? "", effectiveDelimiter).map((_, i) => `col${i + 1}`);
|
|
140
|
+
// #378: apply the same opt-in header sanitization CSVProcessor offers, so
|
|
141
|
+
// the RAG ingestion path doesn't corrupt "Price ($)"-style headers as keys.
|
|
142
|
+
const headers = sanitizeColumnNames
|
|
143
|
+
? dedupeColumnNames(rawHeaders.map((h, i) => sanitizeColumnName(h, columnNameCase, i)))
|
|
144
|
+
: rawHeaders;
|
|
140
145
|
const dataLines = hasHeader ? lines.slice(1) : lines;
|
|
141
146
|
const rows = dataLines.map((line) => splitCSVFields(line, effectiveDelimiter).map((field) => field.trim()));
|
|
142
147
|
let formattedContent;
|
|
143
148
|
switch (outputFormat) {
|
|
144
149
|
case "json":
|
|
145
|
-
formattedContent = JSON.stringify(rows.map((row) =>
|
|
150
|
+
formattedContent = JSON.stringify(rows.map((row) => {
|
|
151
|
+
// Defense-in-depth against a caller-controlled header literally
|
|
152
|
+
// named "__proto__"/"constructor"/"prototype" (#1199);
|
|
153
|
+
// Object.fromEntries can't actually pollute Object.prototype here
|
|
154
|
+
// (spec semantics), but a null-prototype target avoids relying on
|
|
155
|
+
// that for future readers.
|
|
156
|
+
const record = Object.create(null);
|
|
157
|
+
headers.forEach((h, i) => {
|
|
158
|
+
record[h] = row[i];
|
|
159
|
+
});
|
|
160
|
+
return record;
|
|
161
|
+
}), null, 2);
|
|
146
162
|
break;
|
|
147
163
|
case "markdown":
|
|
148
164
|
formattedContent = this.toMarkdownTable(headers, rows);
|
package/dist/types/file.d.ts
CHANGED
|
@@ -80,6 +80,17 @@ export type FileProcessingResult = {
|
|
|
80
80
|
hasHeaders?: boolean;
|
|
81
81
|
/** Detected delimiter */
|
|
82
82
|
detectedDelimiter?: string;
|
|
83
|
+
/** Detected (or overridden) character encoding used to decode the CSV (#362) */
|
|
84
|
+
detectedEncoding?: string;
|
|
85
|
+
/** Confidence (0-100) of the detected encoding (#362) */
|
|
86
|
+
encodingConfidence?: number;
|
|
87
|
+
/** Original→sanitized column-name mapping when sanitizeColumnNames is on (#378) */
|
|
88
|
+
columnNameMapping?: Array<{
|
|
89
|
+
original: string;
|
|
90
|
+
sanitized: string;
|
|
91
|
+
}>;
|
|
92
|
+
/** True when the parse hit its time budget and returned partial rows (#379) */
|
|
93
|
+
parseTimedOut?: boolean;
|
|
83
94
|
version?: string;
|
|
84
95
|
estimatedPages?: number | null;
|
|
85
96
|
provider?: string;
|
|
@@ -125,6 +136,8 @@ export type CSVDataQualityWarning = {
|
|
|
125
136
|
*/
|
|
126
137
|
export type CSVColumnMetadata = {
|
|
127
138
|
name: string;
|
|
139
|
+
/** Original header text before sanitization, when sanitizeColumnNames rewrote it (#378) */
|
|
140
|
+
originalName?: string;
|
|
128
141
|
index: number;
|
|
129
142
|
detectedType: CSVColumnDataType;
|
|
130
143
|
/** Confidence of type detection (0-100) */
|
|
@@ -146,6 +159,17 @@ export type CSVColumnMetadata = {
|
|
|
146
159
|
/** Column name validation issues */
|
|
147
160
|
nameIssues?: string[];
|
|
148
161
|
};
|
|
162
|
+
/** A parsed CSV row: string-keyed with string (or missing) cell values (#384). */
|
|
163
|
+
export type CSVRow = Record<string, string | undefined>;
|
|
164
|
+
/** Result of decoding a buffer with encoding detection (#362). */
|
|
165
|
+
export type DecodedBuffer = {
|
|
166
|
+
/** Decoded text with any BOM removed. */
|
|
167
|
+
text: string;
|
|
168
|
+
/** iconv-lite label actually used to decode. */
|
|
169
|
+
encoding: string;
|
|
170
|
+
/** Detection confidence 0-100 (100 for BOM/override, 0 for the UTF-8 fallback). */
|
|
171
|
+
confidence: number;
|
|
172
|
+
};
|
|
149
173
|
/**
|
|
150
174
|
* CSV processor options
|
|
151
175
|
*/
|
|
@@ -155,6 +179,25 @@ export type CSVProcessorOptions = {
|
|
|
155
179
|
includeHeaders?: boolean;
|
|
156
180
|
sampleDataFormat?: SampleDataFormat;
|
|
157
181
|
extension?: string | null;
|
|
182
|
+
/**
|
|
183
|
+
* Character encoding override (#362). When omitted, the encoding is detected
|
|
184
|
+
* from a BOM then `chardet`, falling back to UTF-8. Accepts any label
|
|
185
|
+
* `iconv-lite` supports (e.g. "utf-8", "utf-16le", "windows-1252", "latin1").
|
|
186
|
+
*/
|
|
187
|
+
encoding?: string;
|
|
188
|
+
/**
|
|
189
|
+
* Rewrite column headers into valid identifiers (#378). Opt-in; default false
|
|
190
|
+
* preserves the raw header strings as object keys.
|
|
191
|
+
*/
|
|
192
|
+
sanitizeColumnNames?: boolean;
|
|
193
|
+
/** Case style used when `sanitizeColumnNames` is on (#378). Default "snake_case". */
|
|
194
|
+
columnNameCase?: "camelCase" | "snake_case";
|
|
195
|
+
/**
|
|
196
|
+
* Wall-clock cap for the streaming parse in milliseconds (#379). On timeout the
|
|
197
|
+
* parse returns the rows collected so far and flags `metadata.parseTimedOut`,
|
|
198
|
+
* rather than hanging forever. Defaults: 30s for strings, 5min for files.
|
|
199
|
+
*/
|
|
200
|
+
parseTimeoutMs?: number;
|
|
158
201
|
};
|
|
159
202
|
/**
|
|
160
203
|
* PDF API types for different providers
|
package/dist/types/generate.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { AvatarOptions, AvatarResult } from "./avatar.js";
|
|
|
15
15
|
import type { MusicOptions, MusicResult } from "./music.js";
|
|
16
16
|
import type { StandardRecord, ValidationSchema, ZodUnknownSchema } from "./aliases.js";
|
|
17
17
|
import type { NeurolinkCredentials } from "./providers.js";
|
|
18
|
-
import type { FileWithMetadata } from "./file.js";
|
|
18
|
+
import type { CSVProcessorOptions, FileWithMetadata } from "./file.js";
|
|
19
19
|
import type { WorkflowConfig } from "./workflow.js";
|
|
20
20
|
import type { Schema, Tool, ToolChoice } from "./tools.js";
|
|
21
21
|
import type { StepResult, LanguageModel } from "./providers.js";
|
|
@@ -123,11 +123,7 @@ export type GenerateOptions = {
|
|
|
123
123
|
*/
|
|
124
124
|
music?: MusicOptions;
|
|
125
125
|
};
|
|
126
|
-
csvOptions?:
|
|
127
|
-
maxRows?: number;
|
|
128
|
-
formatStyle?: "raw" | "markdown" | "json";
|
|
129
|
-
includeHeaders?: boolean;
|
|
130
|
-
};
|
|
126
|
+
csvOptions?: CSVProcessorOptions;
|
|
131
127
|
videoOptions?: {
|
|
132
128
|
frames?: number;
|
|
133
129
|
quality?: number;
|
|
@@ -1133,11 +1129,7 @@ export type TextGenerationOptions = {
|
|
|
1133
1129
|
onError?: OnErrorCallback;
|
|
1134
1130
|
expectedOutcome?: string;
|
|
1135
1131
|
evaluationCriteria?: string[];
|
|
1136
|
-
csvOptions?:
|
|
1137
|
-
maxRows?: number;
|
|
1138
|
-
formatStyle?: "raw" | "markdown" | "json";
|
|
1139
|
-
includeHeaders?: boolean;
|
|
1140
|
-
};
|
|
1132
|
+
csvOptions?: CSVProcessorOptions;
|
|
1141
1133
|
enableSummarization?: boolean;
|
|
1142
1134
|
/**
|
|
1143
1135
|
* Skip injecting tool schemas into the system prompt.
|
package/dist/types/rag.d.ts
CHANGED
|
@@ -500,6 +500,13 @@ export type CSVLoaderOptions = LoaderOptions & {
|
|
|
500
500
|
columns?: string[];
|
|
501
501
|
/** Output format */
|
|
502
502
|
outputFormat?: "text" | "json" | "markdown";
|
|
503
|
+
/**
|
|
504
|
+
* Rewrite headers into valid identifiers (#378). Opt-in; default false keeps
|
|
505
|
+
* the raw header strings as JSON keys / table columns.
|
|
506
|
+
*/
|
|
507
|
+
sanitizeColumnNames?: boolean;
|
|
508
|
+
/** Case style used when `sanitizeColumnNames` is on (#378). Default "snake_case". */
|
|
509
|
+
columnNameCase?: "camelCase" | "snake_case";
|
|
503
510
|
};
|
|
504
511
|
/**
|
|
505
512
|
* Abstract document loader type
|
package/dist/types/stream.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { AIModelProviderConfig, NeurolinkCredentials } from "./providers.js
|
|
|
14
14
|
import type { TTSChunk, TTSOptions, TTSResult } from "./tts.js";
|
|
15
15
|
import type { STTOptions, STTResult } from "./stt.js";
|
|
16
16
|
import type { StandardRecord, ValidationSchema } from "./aliases.js";
|
|
17
|
-
import type { FileWithMetadata } from "./file.js";
|
|
17
|
+
import type { CSVProcessorOptions, FileWithMetadata } from "./file.js";
|
|
18
18
|
import type { WorkflowConfig } from "./workflow.js";
|
|
19
19
|
import type { LanguageModel, StepResult } from "./providers.js";
|
|
20
20
|
import type { Tool, ToolChoice } from "./tools.js";
|
|
@@ -205,11 +205,7 @@ export type StreamOptions = {
|
|
|
205
205
|
enableProgress?: boolean;
|
|
206
206
|
};
|
|
207
207
|
};
|
|
208
|
-
csvOptions?:
|
|
209
|
-
maxRows?: number;
|
|
210
|
-
formatStyle?: "raw" | "markdown" | "json";
|
|
211
|
-
includeHeaders?: boolean;
|
|
212
|
-
};
|
|
208
|
+
csvOptions?: CSVProcessorOptions;
|
|
213
209
|
videoOptions?: {
|
|
214
210
|
frames?: number;
|
|
215
211
|
quality?: number;
|
|
@@ -3,7 +3,33 @@
|
|
|
3
3
|
* Converts CSV files to LLM-friendly text formats
|
|
4
4
|
* Uses streaming for memory efficiency with large files
|
|
5
5
|
*/
|
|
6
|
-
import type { FileProcessingResult, CSVProcessorOptions } from "../types/index.js";
|
|
6
|
+
import type { FileProcessingResult, CSVProcessorOptions, CSVRow } from "../types/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* Rewrite a raw header into a valid identifier. Trims, replaces non-identifier
|
|
9
|
+
* runs with `_`, prefixes a `col_` when it starts with a digit or is empty,
|
|
10
|
+
* then applies the requested case style. Never returns an empty string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sanitizeColumnName(name: string, style?: "camelCase" | "snake_case", index?: number): string;
|
|
13
|
+
/**
|
|
14
|
+
* Deduplicate a list of (already sanitized) names by suffixing `_2`, `_3`, … on
|
|
15
|
+
* collisions, preserving order. The suffixed candidate is bumped until it does
|
|
16
|
+
* not collide with ANY already-emitted name — so an existing `name_2` can't be
|
|
17
|
+
* silently overwritten by a generated `name_2` (which the naive counter did for
|
|
18
|
+
* `["name","name","name_2"]`).
|
|
19
|
+
*/
|
|
20
|
+
export declare function dedupeColumnNames(names: string[]): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Predicate form of the row-shape invariant: a non-null, non-array object whose
|
|
23
|
+
* every value is a string or undefined. csv-parser's default output always
|
|
24
|
+
* satisfies this; the guard is defense-in-depth at the parse boundary.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isValidCsvRow(row: unknown): row is CSVRow;
|
|
27
|
+
/**
|
|
28
|
+
* Assertion form used inside the streaming `data` handlers. Throws a
|
|
29
|
+
* `[CSVProcessor]`-prefixed error (so existing `includes("CSV")` catches still
|
|
30
|
+
* match) when a row violates the invariant.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertValidCsvRow(row: unknown, rowNumber: number): asserts row is CSVRow;
|
|
7
33
|
/**
|
|
8
34
|
* Detect if first line is CSV metadata (not actual data/headers)
|
|
9
35
|
* Common patterns:
|
|
@@ -94,25 +120,57 @@ export declare class CSVProcessor {
|
|
|
94
120
|
* @param maxRows - Maximum rows to parse (default: 1000)
|
|
95
121
|
* @returns Array of row objects
|
|
96
122
|
*/
|
|
97
|
-
static parseCSVFile(filePath: string, maxRows?: number): Promise<
|
|
123
|
+
static parseCSVFile(filePath: string, maxRows?: number, timeoutMs?: number): Promise<CSVRow[]>;
|
|
124
|
+
/**
|
|
125
|
+
* File-parse variant that also reports whether the parse timed out, used by
|
|
126
|
+
* `process()` to surface `metadata.parseTimedOut`.
|
|
127
|
+
*/
|
|
128
|
+
static parseCSVFileWithMeta(filePath: string, maxRows?: number, timeoutMs?: number, encoding?: string): Promise<{
|
|
129
|
+
rows: CSVRow[];
|
|
130
|
+
timedOut: boolean;
|
|
131
|
+
}>;
|
|
132
|
+
/**
|
|
133
|
+
* Peek the head of a raw file stream to resolve encoding, metadata-line
|
|
134
|
+
* skipping, and the delimiter (#361), then return a decoded text stream
|
|
135
|
+
* that still delivers the whole file from byte 0 (via `unshift`) — a
|
|
136
|
+
* single disk read (#368/#362).
|
|
137
|
+
*/
|
|
138
|
+
private static prepareFileSource;
|
|
98
139
|
/**
|
|
99
140
|
* Parse CSV string to array of row objects
|
|
100
141
|
* Exposed for use by tools that need direct CSV parsing
|
|
101
142
|
*
|
|
102
143
|
* @param csvString - CSV data as string
|
|
103
144
|
* @param maxRows - Maximum rows to parse (default: 1000)
|
|
145
|
+
* @param timeoutMs - Wall-clock parse cap; returns partial rows on timeout
|
|
104
146
|
* @returns Array of row objects
|
|
105
147
|
*/
|
|
106
|
-
static parseCSVString(csvString: string, maxRows?: number,
|
|
148
|
+
static parseCSVString(csvString: string, maxRows?: number, timeoutMs?: number): Promise<CSVRow[]>;
|
|
107
149
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
150
|
+
* String-parse variant that also reports whether the parse timed out, used by
|
|
151
|
+
* `process()` to surface `metadata.parseTimedOut`.
|
|
152
|
+
*/
|
|
153
|
+
static parseCSVStringWithMeta(csvString: string, maxRows?: number, timeoutMs?: number): Promise<{
|
|
154
|
+
rows: CSVRow[];
|
|
155
|
+
timedOut: boolean;
|
|
156
|
+
}>;
|
|
157
|
+
/**
|
|
158
|
+
* Shared streaming consumer for both parse methods. Guarantees:
|
|
159
|
+
* - source, rawSource, and parser are all destroyed on ANY settle path
|
|
160
|
+
* (#371) — `rawSource` matters for the file-parse path, where `source`
|
|
161
|
+
* is a decode Transform piped FROM `rawSource`; `.pipe()` does not
|
|
162
|
+
* propagate `.destroy()` upstream, so without this the underlying
|
|
163
|
+
* `fs.createReadStream` fd would leak on abort/timeout/error,
|
|
164
|
+
* - a wall-clock timeout returns partial rows instead of hanging (#379),
|
|
165
|
+
* - every row is validated at the boundary (#384),
|
|
166
|
+
* - reject messages carry columns/last-row-shape/format context (#375),
|
|
167
|
+
* - the delimiter (#361, comma/tab/semicolon/pipe) detected by the caller
|
|
168
|
+
* is honored, instead of csv-parser's hard-coded comma default.
|
|
169
|
+
*
|
|
170
|
+
* @param rawSource - The original file stream `source` was piped from, when
|
|
171
|
+
* applicable (file-parse path only; `undefined` for string parsing).
|
|
114
172
|
*/
|
|
115
|
-
private static
|
|
173
|
+
private static streamParse;
|
|
116
174
|
/**
|
|
117
175
|
* Format parsed CSV data for LLM consumption
|
|
118
176
|
* Only used for JSON and Markdown formats (raw format handled separately)
|