kirograph 0.16.1 → 0.17.0
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/README.md +225 -8
- package/dist/bin/commands/affected.js +40 -5
- package/dist/bin/commands/affected.js.map +2 -2
- package/dist/bin/commands/data.js +665 -0
- package/dist/bin/commands/data.js.map +7 -0
- package/dist/bin/commands/help.js +5 -0
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +28 -1
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/index.js +28 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/steering.js +33 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/index.js.map +1 -1
- package/dist/bin/installer/targets/kiro.js +2 -2
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/kirograph.js +3 -1
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/bin/progress.js +30 -0
- package/dist/bin/progress.js.map +2 -2
- package/dist/compression/naive-cost.js +25 -0
- package/dist/compression/naive-cost.js.map +2 -2
- package/dist/compression/tracker.js +11 -2
- package/dist/compression/tracker.js.map +2 -2
- package/dist/compression/types.js.map +1 -1
- package/dist/config.js +37 -1
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +56 -2
- package/dist/core/pipeline.js.map +2 -2
- package/dist/data/filters.js +105 -0
- package/dist/data/filters.js.map +7 -0
- package/dist/data/indexer.js +225 -0
- package/dist/data/indexer.js.map +7 -0
- package/dist/data/linker.js +150 -0
- package/dist/data/linker.js.map +7 -0
- package/dist/data/lint.js +109 -0
- package/dist/data/lint.js.map +7 -0
- package/dist/data/parsers/csv.js +132 -0
- package/dist/data/parsers/csv.js.map +7 -0
- package/dist/data/parsers/excel.js +88 -0
- package/dist/data/parsers/excel.js.map +7 -0
- package/dist/data/parsers/index.js +79 -0
- package/dist/data/parsers/index.js.map +7 -0
- package/dist/data/parsers/json-array.js +89 -0
- package/dist/data/parsers/json-array.js.map +7 -0
- package/dist/data/parsers/jsonl.js +84 -0
- package/dist/data/parsers/jsonl.js.map +7 -0
- package/dist/data/parsers/parquet.js +95 -0
- package/dist/data/parsers/parquet.js.map +7 -0
- package/dist/data/profiler.js +182 -0
- package/dist/data/profiler.js.map +7 -0
- package/dist/data/queries.js +512 -0
- package/dist/data/queries.js.map +7 -0
- package/dist/data/types.js +17 -0
- package/dist/data/types.js.map +7 -0
- package/dist/db/data-schema.sql +61 -0
- package/dist/db/database.js +11 -0
- package/dist/db/database.js.map +2 -2
- package/dist/mcp/tool-names.js +9 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +423 -0
- package/dist/mcp/tools.js.map +2 -2
- package/dist/types.js.map +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var lint_exports = {};
|
|
30
|
+
__export(lint_exports, {
|
|
31
|
+
DataLinter: () => DataLinter
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(lint_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
var crypto = __toESM(require("crypto"));
|
|
37
|
+
class DataLinter {
|
|
38
|
+
constructor(db, projectRoot) {
|
|
39
|
+
this.db = db;
|
|
40
|
+
this.projectRoot = projectRoot;
|
|
41
|
+
}
|
|
42
|
+
lint() {
|
|
43
|
+
const issues = [];
|
|
44
|
+
const datasets = this.db.all("SELECT * FROM data_datasets");
|
|
45
|
+
let healthy = 0;
|
|
46
|
+
for (const ds of datasets) {
|
|
47
|
+
const dsIssues = [];
|
|
48
|
+
const absPath = path.join(this.projectRoot, ds.file_path);
|
|
49
|
+
if (!fs.existsSync(absPath)) {
|
|
50
|
+
dsIssues.push({ severity: "error", dataset: ds.id, message: `Source file missing: ${ds.file_path}` });
|
|
51
|
+
} else {
|
|
52
|
+
try {
|
|
53
|
+
const content = fs.readFileSync(absPath);
|
|
54
|
+
const currentHash = crypto.createHash("sha256").update(content).digest("hex");
|
|
55
|
+
if (currentHash !== ds.content_hash) {
|
|
56
|
+
dsIssues.push({ severity: "warning", dataset: ds.id, message: `Content changed since last index (hash mismatch). Run kirograph data reindex.` });
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
dsIssues.push({ severity: "warning", dataset: ds.id, message: `Cannot read source file: ${ds.file_path}` });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const tableName = `data_rows_${ds.id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
|
|
63
|
+
try {
|
|
64
|
+
const countRow = this.db.get(`SELECT COUNT(*) as cnt FROM "${tableName}"`);
|
|
65
|
+
const actualRows = countRow?.cnt ?? 0;
|
|
66
|
+
if (actualRows !== ds.row_count) {
|
|
67
|
+
dsIssues.push({ severity: "error", dataset: ds.id, message: `Row count mismatch: metadata says ${ds.row_count}, table has ${actualRows}` });
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
dsIssues.push({ severity: "error", dataset: ds.id, message: `Row table "${tableName}" does not exist` });
|
|
71
|
+
}
|
|
72
|
+
const profileCount = this.db.get("SELECT COUNT(*) as cnt FROM data_columns WHERE dataset_id = ?", [ds.id])?.cnt ?? 0;
|
|
73
|
+
if (profileCount !== ds.column_count) {
|
|
74
|
+
dsIssues.push({ severity: "warning", dataset: ds.id, message: `Column count mismatch: metadata says ${ds.column_count}, profiles has ${profileCount}` });
|
|
75
|
+
}
|
|
76
|
+
if (dsIssues.length === 0) {
|
|
77
|
+
healthy++;
|
|
78
|
+
}
|
|
79
|
+
issues.push(...dsIssues);
|
|
80
|
+
}
|
|
81
|
+
const orphanRefs = this.db.all(`
|
|
82
|
+
SELECT r.dataset_id, r.qualified_name FROM data_code_refs r
|
|
83
|
+
LEFT JOIN data_datasets d ON r.dataset_id = d.id
|
|
84
|
+
WHERE d.id IS NULL
|
|
85
|
+
`);
|
|
86
|
+
if (orphanRefs.length > 0) {
|
|
87
|
+
issues.push({ severity: "warning", message: `${orphanRefs.length} orphan code ref(s) pointing to deleted datasets` });
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
require.resolve("xlsx");
|
|
91
|
+
} catch {
|
|
92
|
+
const xlsxFiles = this.db.all(`SELECT file_path FROM data_datasets WHERE format IN ('xlsx', 'xls')`);
|
|
93
|
+
if (xlsxFiles.length === 0) {
|
|
94
|
+
issues.push({ severity: "info", message: `Optional dep 'xlsx' not installed. Excel files (.xlsx/.xls) will be skipped during indexing.` });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
require.resolve("parquetjs-lite");
|
|
99
|
+
} catch {
|
|
100
|
+
issues.push({ severity: "info", message: `Optional dep 'parquetjs-lite' not installed. Parquet files will be skipped during indexing.` });
|
|
101
|
+
}
|
|
102
|
+
return { issues, datasetsChecked: datasets.length, healthy };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
106
|
+
0 && (module.exports = {
|
|
107
|
+
DataLinter
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/data/lint.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Data Lint\n *\n * Integrity checks for the data index:\n * - Row count mismatch (stored vs actual table rows)\n * - Schema consistency (columns in profile vs actual table)\n * - Stale datasets (source file deleted or changed)\n * - Missing optional deps (files skipped due to missing xlsx/parquetjs-lite)\n * - Orphan code refs (refs pointing to deleted datasets)\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as crypto from 'crypto';\n\nexport interface LintIssue {\n severity: 'error' | 'warning' | 'info';\n dataset?: string;\n message: string;\n}\n\nexport interface LintResult {\n issues: LintIssue[];\n datasetsChecked: number;\n healthy: number;\n}\n\nexport class DataLinter {\n private readonly db: any;\n private readonly projectRoot: string;\n\n constructor(db: any, projectRoot: string) {\n this.db = db;\n this.projectRoot = projectRoot;\n }\n\n lint(): LintResult {\n const issues: LintIssue[] = [];\n const datasets = this.db.all('SELECT * FROM data_datasets') as any[];\n let healthy = 0;\n\n for (const ds of datasets) {\n const dsIssues: LintIssue[] = [];\n\n // Check source file exists\n const absPath = path.join(this.projectRoot, ds.file_path);\n if (!fs.existsSync(absPath)) {\n dsIssues.push({ severity: 'error', dataset: ds.id, message: `Source file missing: ${ds.file_path}` });\n } else {\n // Check content hash (stale detection)\n try {\n const content = fs.readFileSync(absPath);\n const currentHash = crypto.createHash('sha256').update(content).digest('hex');\n if (currentHash !== ds.content_hash) {\n dsIssues.push({ severity: 'warning', dataset: ds.id, message: `Content changed since last index (hash mismatch). Run kirograph data reindex.` });\n }\n } catch {\n dsIssues.push({ severity: 'warning', dataset: ds.id, message: `Cannot read source file: ${ds.file_path}` });\n }\n }\n\n // Check row count matches actual table\n const tableName = `data_rows_${ds.id.replace(/[^a-zA-Z0-9_]/g, '_')}`;\n try {\n const countRow = this.db.get(`SELECT COUNT(*) as cnt FROM \"${tableName}\"`);\n const actualRows = countRow?.cnt ?? 0;\n if (actualRows !== ds.row_count) {\n dsIssues.push({ severity: 'error', dataset: ds.id, message: `Row count mismatch: metadata says ${ds.row_count}, table has ${actualRows}` });\n }\n } catch {\n dsIssues.push({ severity: 'error', dataset: ds.id, message: `Row table \"${tableName}\" does not exist` });\n }\n\n // Check column count matches profiles\n const profileCount = this.db.get('SELECT COUNT(*) as cnt FROM data_columns WHERE dataset_id = ?', [ds.id])?.cnt ?? 0;\n if (profileCount !== ds.column_count) {\n dsIssues.push({ severity: 'warning', dataset: ds.id, message: `Column count mismatch: metadata says ${ds.column_count}, profiles has ${profileCount}` });\n }\n\n if (dsIssues.length === 0) {\n healthy++;\n }\n issues.push(...dsIssues);\n }\n\n // Check for orphan code refs\n const orphanRefs = this.db.all(`\n SELECT r.dataset_id, r.qualified_name FROM data_code_refs r\n LEFT JOIN data_datasets d ON r.dataset_id = d.id\n WHERE d.id IS NULL\n `) as any[];\n\n if (orphanRefs.length > 0) {\n issues.push({ severity: 'warning', message: `${orphanRefs.length} orphan code ref(s) pointing to deleted datasets` });\n }\n\n // Check for missing optional deps\n try {\n require.resolve('xlsx');\n } catch {\n // Check if any xlsx/xls files exist in include patterns\n const xlsxFiles = this.db.all(`SELECT file_path FROM data_datasets WHERE format IN ('xlsx', 'xls')`) as any[];\n if (xlsxFiles.length === 0) {\n // Check if there are xlsx files on disk that were skipped\n issues.push({ severity: 'info', message: `Optional dep 'xlsx' not installed. Excel files (.xlsx/.xls) will be skipped during indexing.` });\n }\n }\n\n try {\n require.resolve('parquetjs-lite');\n } catch {\n issues.push({ severity: 'info', message: `Optional dep 'parquetjs-lite' not installed. Parquet files will be skipped during indexing.` });\n }\n\n return { issues, datasetsChecked: datasets.length, healthy };\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,SAAoB;AACpB,WAAsB;AACtB,aAAwB;AAcjB,MAAM,WAAW;AAAA,EAItB,YAAY,IAAS,aAAqB;AACxC,SAAK,KAAK;AACV,SAAK,cAAc;AAAA,EACrB;AAAA,EAEA,OAAmB;AACjB,UAAM,SAAsB,CAAC;AAC7B,UAAM,WAAW,KAAK,GAAG,IAAI,6BAA6B;AAC1D,QAAI,UAAU;AAEd,eAAW,MAAM,UAAU;AACzB,YAAM,WAAwB,CAAC;AAG/B,YAAM,UAAU,KAAK,KAAK,KAAK,aAAa,GAAG,SAAS;AACxD,UAAI,CAAC,GAAG,WAAW,OAAO,GAAG;AAC3B,iBAAS,KAAK,EAAE,UAAU,SAAS,SAAS,GAAG,IAAI,SAAS,wBAAwB,GAAG,SAAS,GAAG,CAAC;AAAA,MACtG,OAAO;AAEL,YAAI;AACF,gBAAM,UAAU,GAAG,aAAa,OAAO;AACvC,gBAAM,cAAc,OAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC5E,cAAI,gBAAgB,GAAG,cAAc;AACnC,qBAAS,KAAK,EAAE,UAAU,WAAW,SAAS,GAAG,IAAI,SAAS,gFAAgF,CAAC;AAAA,UACjJ;AAAA,QACF,QAAQ;AACN,mBAAS,KAAK,EAAE,UAAU,WAAW,SAAS,GAAG,IAAI,SAAS,4BAA4B,GAAG,SAAS,GAAG,CAAC;AAAA,QAC5G;AAAA,MACF;AAGA,YAAM,YAAY,aAAa,GAAG,GAAG,QAAQ,kBAAkB,GAAG,CAAC;AACnE,UAAI;AACF,cAAM,WAAW,KAAK,GAAG,IAAI,gCAAgC,SAAS,GAAG;AACzE,cAAM,aAAa,UAAU,OAAO;AACpC,YAAI,eAAe,GAAG,WAAW;AAC/B,mBAAS,KAAK,EAAE,UAAU,SAAS,SAAS,GAAG,IAAI,SAAS,qCAAqC,GAAG,SAAS,eAAe,UAAU,GAAG,CAAC;AAAA,QAC5I;AAAA,MACF,QAAQ;AACN,iBAAS,KAAK,EAAE,UAAU,SAAS,SAAS,GAAG,IAAI,SAAS,cAAc,SAAS,mBAAmB,CAAC;AAAA,MACzG;AAGA,YAAM,eAAe,KAAK,GAAG,IAAI,iEAAiE,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;AACnH,UAAI,iBAAiB,GAAG,cAAc;AACpC,iBAAS,KAAK,EAAE,UAAU,WAAW,SAAS,GAAG,IAAI,SAAS,wCAAwC,GAAG,YAAY,kBAAkB,YAAY,GAAG,CAAC;AAAA,MACzJ;AAEA,UAAI,SAAS,WAAW,GAAG;AACzB;AAAA,MACF;AACA,aAAO,KAAK,GAAG,QAAQ;AAAA,IACzB;AAGA,UAAM,aAAa,KAAK,GAAG,IAAI;AAAA;AAAA;AAAA;AAAA,KAI9B;AAED,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,KAAK,EAAE,UAAU,WAAW,SAAS,GAAG,WAAW,MAAM,mDAAmD,CAAC;AAAA,IACtH;AAGA,QAAI;AACF,sBAAgB,MAAM;AAAA,IACxB,QAAQ;AAEN,YAAM,YAAY,KAAK,GAAG,IAAI,qEAAqE;AACnG,UAAI,UAAU,WAAW,GAAG;AAE1B,eAAO,KAAK,EAAE,UAAU,QAAQ,SAAS,+FAA+F,CAAC;AAAA,MAC3I;AAAA,IACF;AAEA,QAAI;AACF,sBAAgB,gBAAgB;AAAA,IAClC,QAAQ;AACN,aAAO,KAAK,EAAE,UAAU,QAAQ,SAAS,8FAA8F,CAAC;AAAA,IAC1I;AAEA,WAAO,EAAE,QAAQ,iBAAiB,SAAS,QAAQ,QAAQ;AAAA,EAC7D;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var csv_exports = {};
|
|
30
|
+
__export(csv_exports, {
|
|
31
|
+
csvParser: () => csvParser,
|
|
32
|
+
tsvParser: () => tsvParser
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(csv_exports);
|
|
35
|
+
var fs = __toESM(require("fs"));
|
|
36
|
+
var readline = __toESM(require("readline"));
|
|
37
|
+
const BATCH_SIZE = 1e4;
|
|
38
|
+
function parseCSVLine(line, delimiter) {
|
|
39
|
+
const fields = [];
|
|
40
|
+
let current = "";
|
|
41
|
+
let inQuotes = false;
|
|
42
|
+
for (let i = 0; i < line.length; i++) {
|
|
43
|
+
const char = line[i];
|
|
44
|
+
if (inQuotes) {
|
|
45
|
+
if (char === '"') {
|
|
46
|
+
if (i + 1 < line.length && line[i + 1] === '"') {
|
|
47
|
+
current += '"';
|
|
48
|
+
i++;
|
|
49
|
+
} else {
|
|
50
|
+
inQuotes = false;
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
current += char;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
if (char === '"') {
|
|
57
|
+
inQuotes = true;
|
|
58
|
+
} else if (char === delimiter) {
|
|
59
|
+
fields.push(current.trim());
|
|
60
|
+
current = "";
|
|
61
|
+
} else {
|
|
62
|
+
current += char;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
fields.push(current.trim());
|
|
67
|
+
return fields;
|
|
68
|
+
}
|
|
69
|
+
function inferValue(raw) {
|
|
70
|
+
if (raw === "" || raw.toLowerCase() === "null" || raw.toLowerCase() === "na" || raw.toLowerCase() === "n/a") return null;
|
|
71
|
+
if (raw.toLowerCase() === "true") return true;
|
|
72
|
+
if (raw.toLowerCase() === "false") return false;
|
|
73
|
+
if (/^-?\d+$/.test(raw)) {
|
|
74
|
+
const n = parseInt(raw, 10);
|
|
75
|
+
if (n >= Number.MIN_SAFE_INTEGER && n <= Number.MAX_SAFE_INTEGER) return n;
|
|
76
|
+
}
|
|
77
|
+
if (/^-?\d+\.\d+$/.test(raw) || /^-?\d+[eE][+-]?\d+$/.test(raw)) {
|
|
78
|
+
const f = parseFloat(raw);
|
|
79
|
+
if (!isNaN(f)) return f;
|
|
80
|
+
}
|
|
81
|
+
return raw;
|
|
82
|
+
}
|
|
83
|
+
async function parseFile(filePath, delimiter, opts) {
|
|
84
|
+
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
|
|
85
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
86
|
+
let columns = [];
|
|
87
|
+
let totalRows = 0;
|
|
88
|
+
let batch = [];
|
|
89
|
+
let isHeader = true;
|
|
90
|
+
for await (const line of rl) {
|
|
91
|
+
if (line.trim() === "") continue;
|
|
92
|
+
if (isHeader) {
|
|
93
|
+
columns = parseCSVLine(line, delimiter);
|
|
94
|
+
isHeader = false;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (totalRows >= opts.maxRows) break;
|
|
98
|
+
const values = parseCSVLine(line, delimiter);
|
|
99
|
+
const row = {};
|
|
100
|
+
for (let i = 0; i < columns.length; i++) {
|
|
101
|
+
row[columns[i]] = inferValue(values[i] ?? "");
|
|
102
|
+
}
|
|
103
|
+
batch.push(row);
|
|
104
|
+
totalRows++;
|
|
105
|
+
if (batch.length >= BATCH_SIZE) {
|
|
106
|
+
opts.onBatch(batch, columns);
|
|
107
|
+
batch = [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (batch.length > 0) {
|
|
111
|
+
opts.onBatch(batch, columns);
|
|
112
|
+
}
|
|
113
|
+
return { columns, totalRows };
|
|
114
|
+
}
|
|
115
|
+
const csvParser = {
|
|
116
|
+
name: "csv",
|
|
117
|
+
extensions: [".csv"],
|
|
118
|
+
isAvailable: () => true,
|
|
119
|
+
parse: (filePath, opts) => parseFile(filePath, ",", opts)
|
|
120
|
+
};
|
|
121
|
+
const tsvParser = {
|
|
122
|
+
name: "tsv",
|
|
123
|
+
extensions: [".tsv"],
|
|
124
|
+
isAvailable: () => true,
|
|
125
|
+
parse: (filePath, opts) => parseFile(filePath, " ", opts)
|
|
126
|
+
};
|
|
127
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
128
|
+
0 && (module.exports = {
|
|
129
|
+
csvParser,
|
|
130
|
+
tsvParser
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/data/parsers/csv.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * CSV/TSV Streaming Parser\n *\n * Handles: .csv, .tsv\n * Parses line-by-line without loading the full file into memory.\n * Handles quoted fields, newlines within quotes, and various delimiters.\n */\n\nimport * as fs from 'fs';\nimport * as readline from 'readline';\nimport type { DataFormatParser, ParsedRow } from '../types';\n\nconst BATCH_SIZE = 10_000;\n\nfunction parseCSVLine(line: string, delimiter: string): string[] {\n const fields: string[] = [];\n let current = '';\n let inQuotes = false;\n\n for (let i = 0; i < line.length; i++) {\n const char = line[i];\n\n if (inQuotes) {\n if (char === '\"') {\n if (i + 1 < line.length && line[i + 1] === '\"') {\n current += '\"';\n i++; // skip escaped quote\n } else {\n inQuotes = false;\n }\n } else {\n current += char;\n }\n } else {\n if (char === '\"') {\n inQuotes = true;\n } else if (char === delimiter) {\n fields.push(current.trim());\n current = '';\n } else {\n current += char;\n }\n }\n }\n fields.push(current.trim());\n return fields;\n}\n\nfunction inferValue(raw: string): string | number | boolean | null {\n if (raw === '' || raw.toLowerCase() === 'null' || raw.toLowerCase() === 'na' || raw.toLowerCase() === 'n/a') return null;\n if (raw.toLowerCase() === 'true') return true;\n if (raw.toLowerCase() === 'false') return false;\n\n // Try integer\n if (/^-?\\d+$/.test(raw)) {\n const n = parseInt(raw, 10);\n if (n >= Number.MIN_SAFE_INTEGER && n <= Number.MAX_SAFE_INTEGER) return n;\n }\n\n // Try float\n if (/^-?\\d+\\.\\d+$/.test(raw) || /^-?\\d+[eE][+-]?\\d+$/.test(raw)) {\n const f = parseFloat(raw);\n if (!isNaN(f)) return f;\n }\n\n return raw;\n}\n\nasync function parseFile(\n filePath: string,\n delimiter: string,\n opts: { maxRows: number; onBatch: (rows: ParsedRow[], columns: string[]) => void },\n): Promise<{ columns: string[]; totalRows: number }> {\n const stream = fs.createReadStream(filePath, { encoding: 'utf8' });\n const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });\n\n let columns: string[] = [];\n let totalRows = 0;\n let batch: ParsedRow[] = [];\n let isHeader = true;\n\n for await (const line of rl) {\n if (line.trim() === '') continue;\n\n if (isHeader) {\n columns = parseCSVLine(line, delimiter);\n isHeader = false;\n continue;\n }\n\n if (totalRows >= opts.maxRows) break;\n\n const values = parseCSVLine(line, delimiter);\n const row: ParsedRow = {};\n for (let i = 0; i < columns.length; i++) {\n row[columns[i]] = inferValue(values[i] ?? '');\n }\n\n batch.push(row);\n totalRows++;\n\n if (batch.length >= BATCH_SIZE) {\n opts.onBatch(batch, columns);\n batch = [];\n }\n }\n\n if (batch.length > 0) {\n opts.onBatch(batch, columns);\n }\n\n return { columns, totalRows };\n}\n\nexport const csvParser: DataFormatParser = {\n name: 'csv',\n extensions: ['.csv'],\n isAvailable: () => true,\n parse: (filePath, opts) => parseFile(filePath, ',', opts),\n};\n\nexport const tsvParser: DataFormatParser = {\n name: 'tsv',\n extensions: ['.tsv'],\n isAvailable: () => true,\n parse: (filePath, opts) => parseFile(filePath, '\\t', opts),\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var excel_exports = {};
|
|
20
|
+
__export(excel_exports, {
|
|
21
|
+
excelParser: () => excelParser
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(excel_exports);
|
|
24
|
+
const BATCH_SIZE = 1e4;
|
|
25
|
+
let xlsxModule = null;
|
|
26
|
+
let xlsxChecked = false;
|
|
27
|
+
function tryLoadXlsx() {
|
|
28
|
+
if (xlsxChecked) return xlsxModule !== null;
|
|
29
|
+
xlsxChecked = true;
|
|
30
|
+
try {
|
|
31
|
+
xlsxModule = require("xlsx");
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const excelParser = {
|
|
38
|
+
name: "xlsx",
|
|
39
|
+
extensions: [".xlsx", ".xls"],
|
|
40
|
+
isAvailable() {
|
|
41
|
+
return tryLoadXlsx();
|
|
42
|
+
},
|
|
43
|
+
async parse(filePath, opts) {
|
|
44
|
+
if (!tryLoadXlsx()) {
|
|
45
|
+
return { columns: [], totalRows: 0 };
|
|
46
|
+
}
|
|
47
|
+
const workbook = xlsxModule.readFile(filePath, { type: "file", cellDates: true });
|
|
48
|
+
const sheetName = workbook.SheetNames[0];
|
|
49
|
+
if (!sheetName) return { columns: [], totalRows: 0 };
|
|
50
|
+
const sheet = workbook.Sheets[sheetName];
|
|
51
|
+
const jsonData = xlsxModule.utils.sheet_to_json(sheet, { defval: null });
|
|
52
|
+
if (jsonData.length === 0) return { columns: [], totalRows: 0 };
|
|
53
|
+
const columns = Object.keys(jsonData[0]);
|
|
54
|
+
let totalRows = 0;
|
|
55
|
+
let batch = [];
|
|
56
|
+
for (const item of jsonData) {
|
|
57
|
+
if (totalRows >= opts.maxRows) break;
|
|
58
|
+
const row = {};
|
|
59
|
+
for (const col of columns) {
|
|
60
|
+
const val = item[col];
|
|
61
|
+
if (val === null || val === void 0) {
|
|
62
|
+
row[col] = null;
|
|
63
|
+
} else if (val instanceof Date) {
|
|
64
|
+
row[col] = val.toISOString();
|
|
65
|
+
} else if (typeof val === "object") {
|
|
66
|
+
row[col] = JSON.stringify(val);
|
|
67
|
+
} else {
|
|
68
|
+
row[col] = val;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
batch.push(row);
|
|
72
|
+
totalRows++;
|
|
73
|
+
if (batch.length >= BATCH_SIZE) {
|
|
74
|
+
opts.onBatch(batch, columns);
|
|
75
|
+
batch = [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (batch.length > 0) {
|
|
79
|
+
opts.onBatch(batch, columns);
|
|
80
|
+
}
|
|
81
|
+
return { columns, totalRows };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
85
|
+
0 && (module.exports = {
|
|
86
|
+
excelParser
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=excel.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/data/parsers/excel.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Excel Parser (Optional Dependency)\n *\n * Handles: .xlsx, .xls\n * Requires: xlsx package (optional dependency)\n * Gracefully returns isAvailable()=false if not installed.\n */\n\nimport type { DataFormatParser, ParsedRow } from '../types';\n\nconst BATCH_SIZE = 10_000;\n\nlet xlsxModule: any = null;\nlet xlsxChecked = false;\n\nfunction tryLoadXlsx(): boolean {\n if (xlsxChecked) return xlsxModule !== null;\n xlsxChecked = true;\n try {\n xlsxModule = require('xlsx');\n return true;\n } catch {\n return false;\n }\n}\n\nexport const excelParser: DataFormatParser = {\n name: 'xlsx',\n extensions: ['.xlsx', '.xls'],\n\n isAvailable(): boolean {\n return tryLoadXlsx();\n },\n\n async parse(filePath, opts) {\n if (!tryLoadXlsx()) {\n return { columns: [], totalRows: 0 };\n }\n\n const workbook = xlsxModule.readFile(filePath, { type: 'file', cellDates: true });\n const sheetName = workbook.SheetNames[0];\n if (!sheetName) return { columns: [], totalRows: 0 };\n\n const sheet = workbook.Sheets[sheetName];\n const jsonData = xlsxModule.utils.sheet_to_json(sheet, { defval: null }) as Record<string, any>[];\n\n if (jsonData.length === 0) return { columns: [], totalRows: 0 };\n\n const columns = Object.keys(jsonData[0]);\n let totalRows = 0;\n let batch: ParsedRow[] = [];\n\n for (const item of jsonData) {\n if (totalRows >= opts.maxRows) break;\n\n const row: ParsedRow = {};\n for (const col of columns) {\n const val = item[col];\n if (val === null || val === undefined) {\n row[col] = null;\n } else if (val instanceof Date) {\n row[col] = val.toISOString();\n } else if (typeof val === 'object') {\n row[col] = JSON.stringify(val);\n } else {\n row[col] = val;\n }\n }\n\n batch.push(row);\n totalRows++;\n\n if (batch.length >= BATCH_SIZE) {\n opts.onBatch(batch, columns);\n batch = [];\n }\n }\n\n if (batch.length > 0) {\n opts.onBatch(batch, columns);\n }\n\n return { columns, totalRows };\n },\n};\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,MAAM,aAAa;AAEnB,IAAI,aAAkB;AACtB,IAAI,cAAc;AAElB,SAAS,cAAuB;AAC9B,MAAI,YAAa,QAAO,eAAe;AACvC,gBAAc;AACd,MAAI;AACF,iBAAa,QAAQ,MAAM;AAC3B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,MAAM,cAAgC;AAAA,EAC3C,MAAM;AAAA,EACN,YAAY,CAAC,SAAS,MAAM;AAAA,EAE5B,cAAuB;AACrB,WAAO,YAAY;AAAA,EACrB;AAAA,EAEA,MAAM,MAAM,UAAU,MAAM;AAC1B,QAAI,CAAC,YAAY,GAAG;AAClB,aAAO,EAAE,SAAS,CAAC,GAAG,WAAW,EAAE;AAAA,IACrC;AAEA,UAAM,WAAW,WAAW,SAAS,UAAU,EAAE,MAAM,QAAQ,WAAW,KAAK,CAAC;AAChF,UAAM,YAAY,SAAS,WAAW,CAAC;AACvC,QAAI,CAAC,UAAW,QAAO,EAAE,SAAS,CAAC,GAAG,WAAW,EAAE;AAEnD,UAAM,QAAQ,SAAS,OAAO,SAAS;AACvC,UAAM,WAAW,WAAW,MAAM,cAAc,OAAO,EAAE,QAAQ,KAAK,CAAC;AAEvE,QAAI,SAAS,WAAW,EAAG,QAAO,EAAE,SAAS,CAAC,GAAG,WAAW,EAAE;AAE9D,UAAM,UAAU,OAAO,KAAK,SAAS,CAAC,CAAC;AACvC,QAAI,YAAY;AAChB,QAAI,QAAqB,CAAC;AAE1B,eAAW,QAAQ,UAAU;AAC3B,UAAI,aAAa,KAAK,QAAS;AAE/B,YAAM,MAAiB,CAAC;AACxB,iBAAW,OAAO,SAAS;AACzB,cAAM,MAAM,KAAK,GAAG;AACpB,YAAI,QAAQ,QAAQ,QAAQ,QAAW;AACrC,cAAI,GAAG,IAAI;AAAA,QACb,WAAW,eAAe,MAAM;AAC9B,cAAI,GAAG,IAAI,IAAI,YAAY;AAAA,QAC7B,WAAW,OAAO,QAAQ,UAAU;AAClC,cAAI,GAAG,IAAI,KAAK,UAAU,GAAG;AAAA,QAC/B,OAAO;AACL,cAAI,GAAG,IAAI;AAAA,QACb;AAAA,MACF;AAEA,YAAM,KAAK,GAAG;AACd;AAEA,UAAI,MAAM,UAAU,YAAY;AAC9B,aAAK,QAAQ,OAAO,OAAO;AAC3B,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAEA,QAAI,MAAM,SAAS,GAAG;AACpB,WAAK,QAAQ,OAAO,OAAO;AAAA,IAC7B;AAEA,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var parsers_exports = {};
|
|
20
|
+
__export(parsers_exports, {
|
|
21
|
+
getAvailableExtensions: () => getAvailableExtensions,
|
|
22
|
+
getDataParser: () => getDataParser,
|
|
23
|
+
getUnavailableExtensions: () => getUnavailableExtensions,
|
|
24
|
+
isSupportedDataFormat: () => isSupportedDataFormat
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(parsers_exports);
|
|
27
|
+
var import_csv = require("./csv");
|
|
28
|
+
var import_jsonl = require("./jsonl");
|
|
29
|
+
var import_json_array = require("./json-array");
|
|
30
|
+
var import_excel = require("./excel");
|
|
31
|
+
var import_parquet = require("./parquet");
|
|
32
|
+
const allParsers = [
|
|
33
|
+
import_csv.csvParser,
|
|
34
|
+
import_csv.tsvParser,
|
|
35
|
+
import_jsonl.jsonlParser,
|
|
36
|
+
import_json_array.jsonArrayParser,
|
|
37
|
+
import_excel.excelParser,
|
|
38
|
+
import_parquet.parquetParser
|
|
39
|
+
];
|
|
40
|
+
const extensionMap = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const parser of allParsers) {
|
|
42
|
+
for (const ext of parser.extensions) {
|
|
43
|
+
extensionMap.set(ext.toLowerCase(), parser);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function getDataParser(filePath) {
|
|
47
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
48
|
+
const parser = extensionMap.get(ext) ?? null;
|
|
49
|
+
if (!parser) return null;
|
|
50
|
+
if (!parser.isAvailable()) return null;
|
|
51
|
+
return parser;
|
|
52
|
+
}
|
|
53
|
+
function isSupportedDataFormat(filePath) {
|
|
54
|
+
return getDataParser(filePath) !== null;
|
|
55
|
+
}
|
|
56
|
+
function getAvailableExtensions() {
|
|
57
|
+
const available = [];
|
|
58
|
+
for (const [ext, parser] of extensionMap) {
|
|
59
|
+
if (parser.isAvailable()) available.push(ext);
|
|
60
|
+
}
|
|
61
|
+
return available;
|
|
62
|
+
}
|
|
63
|
+
function getUnavailableExtensions() {
|
|
64
|
+
const unavailable = [];
|
|
65
|
+
for (const [ext, parser] of extensionMap) {
|
|
66
|
+
if (!parser.isAvailable()) {
|
|
67
|
+
unavailable.push({ ext, parser: parser.name });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return unavailable;
|
|
71
|
+
}
|
|
72
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
73
|
+
0 && (module.exports = {
|
|
74
|
+
getAvailableExtensions,
|
|
75
|
+
getDataParser,
|
|
76
|
+
getUnavailableExtensions,
|
|
77
|
+
isSupportedDataFormat
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/data/parsers/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Data Format Registry\n *\n * Maps file extensions to their appropriate parser.\n * Gracefully skips formats whose optional dependencies are not installed.\n */\n\nimport type { DataFormatParser } from '../types';\nimport { csvParser, tsvParser } from './csv';\nimport { jsonlParser } from './jsonl';\nimport { jsonArrayParser } from './json-array';\nimport { excelParser } from './excel';\nimport { parquetParser } from './parquet';\n\n// \u2500\u2500 Registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst allParsers: DataFormatParser[] = [\n csvParser,\n tsvParser,\n jsonlParser,\n jsonArrayParser,\n excelParser,\n parquetParser,\n];\n\n/** Extension \u2192 parser lookup */\nconst extensionMap = new Map<string, DataFormatParser>();\nfor (const parser of allParsers) {\n for (const ext of parser.extensions) {\n extensionMap.set(ext.toLowerCase(), parser);\n }\n}\n\n// \u2500\u2500 Public API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Get the parser for a given file extension.\n * Returns null if no parser is registered or the optional dep is missing.\n */\nexport function getDataParser(filePath: string): DataFormatParser | null {\n const ext = filePath.slice(filePath.lastIndexOf('.')).toLowerCase();\n const parser = extensionMap.get(ext) ?? null;\n if (!parser) return null;\n if (!parser.isAvailable()) return null;\n return parser;\n}\n\n/**\n * Check if a file extension is supported for data parsing.\n */\nexport function isSupportedDataFormat(filePath: string): boolean {\n return getDataParser(filePath) !== null;\n}\n\n/**\n * Get all supported extensions (only those with available deps).\n */\nexport function getAvailableExtensions(): string[] {\n const available: string[] = [];\n for (const [ext, parser] of extensionMap) {\n if (parser.isAvailable()) available.push(ext);\n }\n return available;\n}\n\n/**\n * Get extensions that are registered but have missing optional deps.\n */\nexport function getUnavailableExtensions(): Array<{ ext: string; parser: string }> {\n const unavailable: Array<{ ext: string; parser: string }> = [];\n for (const [ext, parser] of extensionMap) {\n if (!parser.isAvailable()) {\n unavailable.push({ ext, parser: parser.name });\n }\n }\n return unavailable;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,iBAAqC;AACrC,mBAA4B;AAC5B,wBAAgC;AAChC,mBAA4B;AAC5B,qBAA8B;AAI9B,MAAM,aAAiC;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,MAAM,eAAe,oBAAI,IAA8B;AACvD,WAAW,UAAU,YAAY;AAC/B,aAAW,OAAO,OAAO,YAAY;AACnC,iBAAa,IAAI,IAAI,YAAY,GAAG,MAAM;AAAA,EAC5C;AACF;AAQO,SAAS,cAAc,UAA2C;AACvE,QAAM,MAAM,SAAS,MAAM,SAAS,YAAY,GAAG,CAAC,EAAE,YAAY;AAClE,QAAM,SAAS,aAAa,IAAI,GAAG,KAAK;AACxC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,CAAC,OAAO,YAAY,EAAG,QAAO;AAClC,SAAO;AACT;AAKO,SAAS,sBAAsB,UAA2B;AAC/D,SAAO,cAAc,QAAQ,MAAM;AACrC;AAKO,SAAS,yBAAmC;AACjD,QAAM,YAAsB,CAAC;AAC7B,aAAW,CAAC,KAAK,MAAM,KAAK,cAAc;AACxC,QAAI,OAAO,YAAY,EAAG,WAAU,KAAK,GAAG;AAAA,EAC9C;AACA,SAAO;AACT;AAKO,SAAS,2BAAmE;AACjF,QAAM,cAAsD,CAAC;AAC7D,aAAW,CAAC,KAAK,MAAM,KAAK,cAAc;AACxC,QAAI,CAAC,OAAO,YAAY,GAAG;AACzB,kBAAY,KAAK,EAAE,KAAK,QAAQ,OAAO,KAAK,CAAC;AAAA,IAC/C;AAAA,EACF;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var json_array_exports = {};
|
|
30
|
+
__export(json_array_exports, {
|
|
31
|
+
jsonArrayParser: () => jsonArrayParser
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(json_array_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
const BATCH_SIZE = 1e4;
|
|
36
|
+
const jsonArrayParser = {
|
|
37
|
+
name: "json",
|
|
38
|
+
extensions: [".json"],
|
|
39
|
+
isAvailable: () => true,
|
|
40
|
+
async parse(filePath, opts) {
|
|
41
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
42
|
+
let data;
|
|
43
|
+
try {
|
|
44
|
+
data = JSON.parse(content);
|
|
45
|
+
} catch {
|
|
46
|
+
return { columns: [], totalRows: 0 };
|
|
47
|
+
}
|
|
48
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
49
|
+
return { columns: [], totalRows: 0 };
|
|
50
|
+
}
|
|
51
|
+
if (typeof data[0] !== "object" || data[0] === null || Array.isArray(data[0])) {
|
|
52
|
+
return { columns: [], totalRows: 0 };
|
|
53
|
+
}
|
|
54
|
+
const columnSet = /* @__PURE__ */ new Set();
|
|
55
|
+
let totalRows = 0;
|
|
56
|
+
let batch = [];
|
|
57
|
+
for (const item of data) {
|
|
58
|
+
if (totalRows >= opts.maxRows) break;
|
|
59
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) continue;
|
|
60
|
+
const row = {};
|
|
61
|
+
for (const [key, value] of Object.entries(item)) {
|
|
62
|
+
columnSet.add(key);
|
|
63
|
+
if (value === null || value === void 0) {
|
|
64
|
+
row[key] = null;
|
|
65
|
+
} else if (typeof value === "object") {
|
|
66
|
+
row[key] = JSON.stringify(value);
|
|
67
|
+
} else {
|
|
68
|
+
row[key] = value;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
batch.push(row);
|
|
72
|
+
totalRows++;
|
|
73
|
+
if (batch.length >= BATCH_SIZE) {
|
|
74
|
+
opts.onBatch(batch, [...columnSet]);
|
|
75
|
+
batch = [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const columns = [...columnSet];
|
|
79
|
+
if (batch.length > 0) {
|
|
80
|
+
opts.onBatch(batch, columns);
|
|
81
|
+
}
|
|
82
|
+
return { columns, totalRows };
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
86
|
+
0 && (module.exports = {
|
|
87
|
+
jsonArrayParser
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=json-array.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/data/parsers/json-array.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * JSON Array Parser\n *\n * Handles: .json files that contain an array of objects\n * For files in data/ directories (not package.json, tsconfig.json, etc.)\n */\n\nimport * as fs from 'fs';\nimport type { DataFormatParser, ParsedRow } from '../types';\n\nconst BATCH_SIZE = 10_000;\n\nexport const jsonArrayParser: DataFormatParser = {\n name: 'json',\n extensions: ['.json'],\n isAvailable: () => true,\n\n async parse(filePath, opts) {\n const content = fs.readFileSync(filePath, 'utf8');\n let data: unknown;\n\n try {\n data = JSON.parse(content);\n } catch {\n return { columns: [], totalRows: 0 };\n }\n\n // Must be an array of objects\n if (!Array.isArray(data) || data.length === 0) {\n return { columns: [], totalRows: 0 };\n }\n\n // Check first element is an object\n if (typeof data[0] !== 'object' || data[0] === null || Array.isArray(data[0])) {\n return { columns: [], totalRows: 0 };\n }\n\n const columnSet = new Set<string>();\n let totalRows = 0;\n let batch: ParsedRow[] = [];\n\n for (const item of data) {\n if (totalRows >= opts.maxRows) break;\n if (typeof item !== 'object' || item === null || Array.isArray(item)) continue;\n\n const row: ParsedRow = {};\n for (const [key, value] of Object.entries(item)) {\n columnSet.add(key);\n if (value === null || value === undefined) {\n row[key] = null;\n } else if (typeof value === 'object') {\n row[key] = JSON.stringify(value);\n } else {\n row[key] = value as string | number | boolean;\n }\n }\n\n batch.push(row);\n totalRows++;\n\n if (batch.length >= BATCH_SIZE) {\n opts.onBatch(batch, [...columnSet]);\n batch = [];\n }\n }\n\n const columns = [...columnSet];\n if (batch.length > 0) {\n opts.onBatch(batch, columns);\n }\n\n return { columns, totalRows };\n },\n};\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAoB;AAGpB,MAAM,aAAa;AAEZ,MAAM,kBAAoC;AAAA,EAC/C,MAAM;AAAA,EACN,YAAY,CAAC,OAAO;AAAA,EACpB,aAAa,MAAM;AAAA,EAEnB,MAAM,MAAM,UAAU,MAAM;AAC1B,UAAM,UAAU,GAAG,aAAa,UAAU,MAAM;AAChD,QAAI;AAEJ,QAAI;AACF,aAAO,KAAK,MAAM,OAAO;AAAA,IAC3B,QAAQ;AACN,aAAO,EAAE,SAAS,CAAC,GAAG,WAAW,EAAE;AAAA,IACrC;AAGA,QAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,GAAG;AAC7C,aAAO,EAAE,SAAS,CAAC,GAAG,WAAW,EAAE;AAAA,IACrC;AAGA,QAAI,OAAO,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,QAAQ,MAAM,QAAQ,KAAK,CAAC,CAAC,GAAG;AAC7E,aAAO,EAAE,SAAS,CAAC,GAAG,WAAW,EAAE;AAAA,IACrC;AAEA,UAAM,YAAY,oBAAI,IAAY;AAClC,QAAI,YAAY;AAChB,QAAI,QAAqB,CAAC;AAE1B,eAAW,QAAQ,MAAM;AACvB,UAAI,aAAa,KAAK,QAAS;AAC/B,UAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,QAAQ,IAAI,EAAG;AAEtE,YAAM,MAAiB,CAAC;AACxB,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,kBAAU,IAAI,GAAG;AACjB,YAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,cAAI,GAAG,IAAI;AAAA,QACb,WAAW,OAAO,UAAU,UAAU;AACpC,cAAI,GAAG,IAAI,KAAK,UAAU,KAAK;AAAA,QACjC,OAAO;AACL,cAAI,GAAG,IAAI;AAAA,QACb;AAAA,MACF;AAEA,YAAM,KAAK,GAAG;AACd;AAEA,UAAI,MAAM,UAAU,YAAY;AAC9B,aAAK,QAAQ,OAAO,CAAC,GAAG,SAAS,CAAC;AAClC,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AAEA,UAAM,UAAU,CAAC,GAAG,SAAS;AAC7B,QAAI,MAAM,SAAS,GAAG;AACpB,WAAK,QAAQ,OAAO,OAAO;AAAA,IAC7B;AAEA,WAAO,EAAE,SAAS,UAAU;AAAA,EAC9B;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var jsonl_exports = {};
|
|
30
|
+
__export(jsonl_exports, {
|
|
31
|
+
jsonlParser: () => jsonlParser
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(jsonl_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var readline = __toESM(require("readline"));
|
|
36
|
+
const BATCH_SIZE = 1e4;
|
|
37
|
+
const jsonlParser = {
|
|
38
|
+
name: "jsonl",
|
|
39
|
+
extensions: [".jsonl", ".ndjson"],
|
|
40
|
+
isAvailable: () => true,
|
|
41
|
+
async parse(filePath, opts) {
|
|
42
|
+
const stream = fs.createReadStream(filePath, { encoding: "utf8" });
|
|
43
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
44
|
+
const columnSet = /* @__PURE__ */ new Set();
|
|
45
|
+
let totalRows = 0;
|
|
46
|
+
let batch = [];
|
|
47
|
+
for await (const line of rl) {
|
|
48
|
+
if (line.trim() === "") continue;
|
|
49
|
+
if (totalRows >= opts.maxRows) break;
|
|
50
|
+
try {
|
|
51
|
+
const obj = JSON.parse(line);
|
|
52
|
+
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) continue;
|
|
53
|
+
const row = {};
|
|
54
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
55
|
+
columnSet.add(key);
|
|
56
|
+
if (value === null || value === void 0) {
|
|
57
|
+
row[key] = null;
|
|
58
|
+
} else if (typeof value === "object") {
|
|
59
|
+
row[key] = JSON.stringify(value);
|
|
60
|
+
} else {
|
|
61
|
+
row[key] = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
batch.push(row);
|
|
65
|
+
totalRows++;
|
|
66
|
+
if (batch.length >= BATCH_SIZE) {
|
|
67
|
+
opts.onBatch(batch, [...columnSet]);
|
|
68
|
+
batch = [];
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const columns = [...columnSet];
|
|
74
|
+
if (batch.length > 0) {
|
|
75
|
+
opts.onBatch(batch, columns);
|
|
76
|
+
}
|
|
77
|
+
return { columns, totalRows };
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
81
|
+
0 && (module.exports = {
|
|
82
|
+
jsonlParser
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=jsonl.js.map
|