Skip to content

Commit b392b7f

Browse files
committed
feat: enhance rawResultToJSON to support NDJSON and improve error handling
1 parent 24bfa83 commit b392b7f

File tree

1 file changed

+57
-20
lines changed

1 file changed

+57
-20
lines changed

src/store/index.ts

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -483,28 +483,67 @@ interface ExternalQueryResponse {
483483
// Converts a raw result (from an external HTTP endpoint) into a QueryResult.
484484
const rawResultToJSON = (rawResult: string): QueryResult => {
485485
try {
486-
const parsed: unknown = JSON.parse(rawResult);
486+
// Try parsing as single JSON first (standard format)
487+
let parsed: Partial<ExternalQueryResponse>;
487488

488-
if (!parsed || typeof parsed !== 'object') {
489-
throw new Error("Invalid raw result format: not a valid JSON object");
490-
}
489+
try {
490+
parsed = JSON.parse(rawResult);
491+
} catch (singleJsonError) {
492+
// If single JSON fails, try NDJSON (newline-delimited JSON)
493+
// DuckDB httpserver may return multiple JSON objects, one per line
494+
const lines = rawResult.trim().split('\n').filter(line => line.trim());
495+
496+
if (lines.length === 0) {
497+
throw new Error("Empty response");
498+
}
491499

492-
const response = parsed as Partial<ExternalQueryResponse>;
500+
// Parse each line as JSON
501+
const objects = lines.map((line, idx) => {
502+
try {
503+
return JSON.parse(line);
504+
} catch {
505+
throw new Error(`Failed to parse line ${idx + 1}: ${line.substring(0, 50)}...`);
506+
}
507+
});
493508

494-
if (
495-
!response.meta ||
496-
!response.data ||
497-
!Array.isArray(response.meta) ||
498-
!Array.isArray(response.data)
499-
) {
500-
throw new Error(
501-
"Invalid raw result format: meta or data are missing or have the wrong format"
509+
// Find the result object (has meta and data)
510+
const resultObj = objects.find(
511+
(obj): obj is ExternalQueryResponse =>
512+
obj && typeof obj === 'object' && 'meta' in obj && 'data' in obj
502513
);
514+
515+
if (resultObj) {
516+
parsed = resultObj;
517+
} else {
518+
// If no single result object, try to merge (meta from one, data from others)
519+
const metaObj = objects.find(obj => obj?.meta);
520+
const dataObj = objects.find(obj => obj?.data);
521+
522+
if (metaObj && dataObj) {
523+
parsed = {
524+
meta: metaObj.meta,
525+
data: dataObj.data,
526+
rows: dataObj.rows || dataObj.data?.length || 0,
527+
};
528+
} else {
529+
throw singleJsonError; // Re-throw original error
530+
}
531+
}
532+
}
533+
534+
// Validate required fields
535+
if (!parsed || typeof parsed !== 'object') {
536+
throw new Error("Invalid response: not a valid JSON object");
537+
}
538+
539+
if (!parsed.meta || !parsed.data || !Array.isArray(parsed.meta) || !Array.isArray(parsed.data)) {
540+
throw new Error("Invalid response: meta or data missing or wrong format");
503541
}
504542

505-
const columns = response.meta.map(m => m.name);
506-
const columnTypes = response.meta.map(m => m.type);
507-
const data = response.data.map((row: unknown) => {
543+
// Convert to QueryResult format
544+
const columns = parsed.meta.map(m => m.name);
545+
const columnTypes = parsed.meta.map(m => m.type);
546+
const data = parsed.data.map((row: unknown) => {
508547
if (!Array.isArray(row)) {
509548
throw new Error("Invalid row format: expected array");
510549
}
@@ -519,13 +558,11 @@ const rawResultToJSON = (rawResult: string): QueryResult => {
519558
columns,
520559
columnTypes,
521560
data,
522-
rowCount: response.rows || data.length,
561+
rowCount: parsed.rows || data.length,
523562
};
524563
} catch (error) {
525564
throw new Error(
526-
`Failed to parse raw result: ${
527-
error instanceof Error ? error.message : "Unknown error"
528-
}`
565+
`Failed to parse raw result: ${error instanceof Error ? error.message : "Unknown error"}`
529566
);
530567
}
531568
};

0 commit comments

Comments
 (0)