rowpipe 1.0.0 → 1.1.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 +25 -32
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/core/types.d.ts +2 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/readers/csv.d.ts.map +1 -1
- package/dist/readers/csv.js +28 -16
- package/dist/readers/csv.js.map +1 -1
- package/dist/readers/index.d.ts +1 -0
- package/dist/readers/index.d.ts.map +1 -1
- package/dist/readers/index.js +29 -3
- package/dist/readers/index.js.map +1 -1
- package/dist/readers/json.d.ts.map +1 -1
- package/dist/readers/json.js +6 -8
- package/dist/readers/json.js.map +1 -1
- package/dist/readers/jsonl.d.ts.map +1 -1
- package/dist/readers/jsonl.js +5 -7
- package/dist/readers/jsonl.js.map +1 -1
- package/dist/readers/parquet.d.ts +17 -0
- package/dist/readers/parquet.d.ts.map +1 -0
- package/dist/readers/parquet.js +146 -0
- package/dist/readers/parquet.js.map +1 -0
- package/dist/utils/compression.d.ts +30 -6
- package/dist/utils/compression.d.ts.map +1 -1
- package/dist/utils/compression.js +95 -18
- package/dist/utils/compression.js.map +1 -1
- package/dist/writers/csv.d.ts.map +1 -1
- package/dist/writers/csv.js +9 -11
- package/dist/writers/csv.js.map +1 -1
- package/dist/writers/index.d.ts +2 -0
- package/dist/writers/index.d.ts.map +1 -1
- package/dist/writers/index.js +49 -4
- package/dist/writers/index.js.map +1 -1
- package/dist/writers/json.d.ts +1 -1
- package/dist/writers/json.d.ts.map +1 -1
- package/dist/writers/json.js +9 -11
- package/dist/writers/json.js.map +1 -1
- package/dist/writers/jsonl.d.ts +1 -1
- package/dist/writers/jsonl.d.ts.map +1 -1
- package/dist/writers/jsonl.js +9 -11
- package/dist/writers/jsonl.js.map +1 -1
- package/dist/writers/markdown.d.ts +21 -0
- package/dist/writers/markdown.d.ts.map +1 -0
- package/dist/writers/markdown.js +82 -0
- package/dist/writers/markdown.js.map +1 -0
- package/dist/writers/parquet.d.ts +19 -0
- package/dist/writers/parquet.d.ts.map +1 -0
- package/dist/writers/parquet.js +129 -0
- package/dist/writers/parquet.js.map +1 -0
- package/package.json +13 -2
- package/skills/rowpipe/SKILL.md +181 -0
- package/benchmarks/memory-bench.ts +0 -109
- package/src/analytics/reduce.ts +0 -388
- package/src/analytics/schema-inference.ts +0 -217
- package/src/analytics/semantic-types.ts +0 -36
- package/src/analytics/stats.ts +0 -383
- package/src/analytics/validator.ts +0 -134
- package/src/cli/commands/cast.ts +0 -60
- package/src/cli/commands/convert.ts +0 -128
- package/src/cli/commands/filter.ts +0 -58
- package/src/cli/commands/inspect.ts +0 -193
- package/src/cli/commands/map.ts +0 -66
- package/src/cli/commands/reduce.ts +0 -74
- package/src/cli/commands/rename.ts +0 -59
- package/src/cli/commands/sample.ts +0 -61
- package/src/cli/commands/schema.ts +0 -86
- package/src/cli/commands/select.ts +0 -59
- package/src/cli/commands/stats.ts +0 -100
- package/src/cli/commands/validate.ts +0 -123
- package/src/cli/index.ts +0 -285
- package/src/core/batch.ts +0 -81
- package/src/core/errors.ts +0 -81
- package/src/core/pipeline.ts +0 -150
- package/src/core/types.ts +0 -110
- package/src/index.ts +0 -52
- package/src/readers/csv.ts +0 -302
- package/src/readers/index.ts +0 -88
- package/src/readers/json.ts +0 -256
- package/src/readers/jsonl.ts +0 -126
- package/src/readers/xlsx.ts +0 -182
- package/src/transforms/cast.ts +0 -212
- package/src/transforms/expression.ts +0 -1114
- package/src/transforms/filter.ts +0 -38
- package/src/transforms/map.ts +0 -99
- package/src/transforms/rename.ts +0 -47
- package/src/transforms/sample.ts +0 -57
- package/src/transforms/select.ts +0 -64
- package/src/utils/compression.ts +0 -43
- package/src/utils/formatting.ts +0 -75
- package/src/utils/progress.ts +0 -37
- package/src/writers/csv.ts +0 -106
- package/src/writers/index.ts +0 -87
- package/src/writers/json.ts +0 -52
- package/src/writers/jsonl.ts +0 -49
- package/src/writers/xlsx.ts +0 -87
- package/tests/analytics.test.ts +0 -119
- package/tests/cli-integration.test.ts +0 -187
- package/tests/map-reduce.test.ts +0 -223
- package/tests/readers-writers.test.ts +0 -189
- package/tests/transforms.test.ts +0 -199
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -9
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { SchemaInferenceAggregator } from "../../analytics/schema-inference.js";
|
|
2
|
-
import { createPipeline } from "../../core/pipeline.js";
|
|
3
|
-
import { createReader, inferFormatFromPath } from "../../readers/index.js";
|
|
4
|
-
import { openReadableStream } from "../../utils/compression.js";
|
|
5
|
-
import { formatTable, logMemoryDebug } from "../../utils/formatting.js";
|
|
6
|
-
import { ProgressReporter } from "../../utils/progress.js";
|
|
7
|
-
|
|
8
|
-
export interface SchemaCommandOptions {
|
|
9
|
-
from?: string;
|
|
10
|
-
sheet?: string;
|
|
11
|
-
path?: string;
|
|
12
|
-
delimiter?: string;
|
|
13
|
-
sample?: string | number;
|
|
14
|
-
full?: boolean;
|
|
15
|
-
json?: boolean;
|
|
16
|
-
quiet?: boolean;
|
|
17
|
-
noProgress?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function schemaCommand(
|
|
21
|
-
inputPath = "-",
|
|
22
|
-
options: SchemaCommandOptions = {}
|
|
23
|
-
): Promise<void> {
|
|
24
|
-
const progress = new ProgressReporter(options);
|
|
25
|
-
|
|
26
|
-
let fromFormat = options.from?.toLowerCase();
|
|
27
|
-
if (!fromFormat && inputPath !== "-") {
|
|
28
|
-
fromFormat = inferFormatFromPath(inputPath) ?? undefined;
|
|
29
|
-
}
|
|
30
|
-
if (!fromFormat) {
|
|
31
|
-
fromFormat = "csv";
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const sampleSize = options.full
|
|
35
|
-
? Number.POSITIVE_INFINITY
|
|
36
|
-
: options.sample !== undefined
|
|
37
|
-
? Number(options.sample)
|
|
38
|
-
: 10000;
|
|
39
|
-
|
|
40
|
-
const inputStream = openReadableStream(inputPath);
|
|
41
|
-
const reader = createReader(inputStream, {
|
|
42
|
-
format: fromFormat,
|
|
43
|
-
sheet: options.sheet,
|
|
44
|
-
path: options.path,
|
|
45
|
-
delimiter: options.delimiter,
|
|
46
|
-
filePath: inputPath,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const schemaAgg = new SchemaInferenceAggregator({ sample: sampleSize });
|
|
50
|
-
const pipeline = createPipeline(reader);
|
|
51
|
-
pipeline.onProgress((info) => progress.update(info));
|
|
52
|
-
|
|
53
|
-
for await (const row of pipeline.rows()) {
|
|
54
|
-
schemaAgg.add(row);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
progress.done();
|
|
58
|
-
const res = schemaAgg.result();
|
|
59
|
-
|
|
60
|
-
if (options.json) {
|
|
61
|
-
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
62
|
-
} else {
|
|
63
|
-
const tableRows = res.columns.map((col) => {
|
|
64
|
-
let typeDisplay = col.type as string;
|
|
65
|
-
if (col.semantic) {
|
|
66
|
-
typeDisplay += ` (${col.semantic})`;
|
|
67
|
-
}
|
|
68
|
-
return [
|
|
69
|
-
col.name,
|
|
70
|
-
typeDisplay,
|
|
71
|
-
col.nullable ? "true" : "false",
|
|
72
|
-
`${col.confidence}%`,
|
|
73
|
-
];
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
process.stdout.write(
|
|
77
|
-
formatTable(
|
|
78
|
-
["COLUMN", "TYPE", "NULLABLE", "CONFIDENCE"],
|
|
79
|
-
tableRows,
|
|
80
|
-
["left", "left", "left", "right"]
|
|
81
|
-
) + "\n"
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
logMemoryDebug();
|
|
86
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { createPipeline } from "../../core/pipeline.js";
|
|
2
|
-
import { createReader, inferFormatFromPath as inferReaderFormat } from "../../readers/index.js";
|
|
3
|
-
import { createWriter } from "../../writers/index.js";
|
|
4
|
-
import { selectColumns } from "../../transforms/select.js";
|
|
5
|
-
import { openReadableStream } from "../../utils/compression.js";
|
|
6
|
-
import { logMemoryDebug } from "../../utils/formatting.js";
|
|
7
|
-
import { ProgressReporter } from "../../utils/progress.js";
|
|
8
|
-
|
|
9
|
-
export interface SelectCommandOptions {
|
|
10
|
-
from?: string;
|
|
11
|
-
to?: string;
|
|
12
|
-
sheet?: string;
|
|
13
|
-
delimiter?: string;
|
|
14
|
-
batchSize?: string | number;
|
|
15
|
-
quiet?: boolean;
|
|
16
|
-
noProgress?: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export async function selectCommand(
|
|
20
|
-
inputPath = "-",
|
|
21
|
-
columnsArg: string,
|
|
22
|
-
options: SelectCommandOptions = {}
|
|
23
|
-
): Promise<void> {
|
|
24
|
-
const effectiveBatchSize = Number(options.batchSize) || 1000;
|
|
25
|
-
const progress = new ProgressReporter(options);
|
|
26
|
-
|
|
27
|
-
let fromFormat = options.from?.toLowerCase();
|
|
28
|
-
if (!fromFormat && inputPath !== "-") {
|
|
29
|
-
fromFormat = inferReaderFormat(inputPath) ?? undefined;
|
|
30
|
-
}
|
|
31
|
-
if (!fromFormat) {
|
|
32
|
-
fromFormat = "csv";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const toFormat = options.to?.toLowerCase() || (fromFormat === "xlsx" ? "csv" : fromFormat);
|
|
36
|
-
const columns = columnsArg.split(",").map((c) => c.trim());
|
|
37
|
-
|
|
38
|
-
const inputStream = openReadableStream(inputPath);
|
|
39
|
-
const reader = createReader(inputStream, {
|
|
40
|
-
format: fromFormat,
|
|
41
|
-
sheet: options.sheet,
|
|
42
|
-
delimiter: options.delimiter,
|
|
43
|
-
batchSize: effectiveBatchSize,
|
|
44
|
-
filePath: inputPath,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const writer = createWriter(process.stdout, {
|
|
48
|
-
format: toFormat,
|
|
49
|
-
delimiter: options.delimiter,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const pipeline = createPipeline(reader, { batchSize: effectiveBatchSize });
|
|
53
|
-
pipeline.pipe(selectColumns(columns));
|
|
54
|
-
pipeline.onProgress((info) => progress.update(info));
|
|
55
|
-
|
|
56
|
-
await pipeline.to(writer);
|
|
57
|
-
progress.done();
|
|
58
|
-
logMemoryDebug();
|
|
59
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { DatasetStatsAggregator } from "../../analytics/stats.js";
|
|
2
|
-
import { createPipeline } from "../../core/pipeline.js";
|
|
3
|
-
import { createReader, inferFormatFromPath } from "../../readers/index.js";
|
|
4
|
-
import { openReadableStream } from "../../utils/compression.js";
|
|
5
|
-
import { formatDecimal, formatNumber, logMemoryDebug } from "../../utils/formatting.js";
|
|
6
|
-
import { ProgressReporter } from "../../utils/progress.js";
|
|
7
|
-
|
|
8
|
-
export interface StatsCommandOptions {
|
|
9
|
-
column?: string;
|
|
10
|
-
fast?: boolean;
|
|
11
|
-
exact?: boolean;
|
|
12
|
-
json?: boolean;
|
|
13
|
-
from?: string;
|
|
14
|
-
sheet?: string;
|
|
15
|
-
path?: string;
|
|
16
|
-
delimiter?: string;
|
|
17
|
-
batchSize?: string | number;
|
|
18
|
-
quiet?: boolean;
|
|
19
|
-
noProgress?: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export async function statsCommand(
|
|
23
|
-
inputPath = "-",
|
|
24
|
-
options: StatsCommandOptions = {}
|
|
25
|
-
): Promise<void> {
|
|
26
|
-
const progress = new ProgressReporter(options);
|
|
27
|
-
|
|
28
|
-
let fromFormat = options.from?.toLowerCase();
|
|
29
|
-
if (!fromFormat && inputPath !== "-") {
|
|
30
|
-
fromFormat = inferFormatFromPath(inputPath) ?? undefined;
|
|
31
|
-
}
|
|
32
|
-
if (!fromFormat) {
|
|
33
|
-
fromFormat = "csv";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const inputStream = openReadableStream(inputPath);
|
|
37
|
-
const reader = createReader(inputStream, {
|
|
38
|
-
format: fromFormat,
|
|
39
|
-
sheet: options.sheet,
|
|
40
|
-
path: options.path,
|
|
41
|
-
delimiter: options.delimiter,
|
|
42
|
-
batchSize: Number(options.batchSize) || 2000,
|
|
43
|
-
filePath: inputPath,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const statsAgg = new DatasetStatsAggregator({ column: options.column });
|
|
47
|
-
const pipeline = createPipeline(reader);
|
|
48
|
-
pipeline.onProgress((info) => progress.update(info));
|
|
49
|
-
|
|
50
|
-
for await (const row of pipeline.rows()) {
|
|
51
|
-
statsAgg.add(row);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
progress.done();
|
|
55
|
-
const res = statsAgg.result();
|
|
56
|
-
|
|
57
|
-
if (options.json) {
|
|
58
|
-
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
59
|
-
} else {
|
|
60
|
-
process.stdout.write(`Total Rows: ${formatNumber(res.totalRows)}\n\n`);
|
|
61
|
-
|
|
62
|
-
for (const [colName, colStat] of Object.entries(res.columns)) {
|
|
63
|
-
process.stdout.write(`${colName} (${colStat.type})\n`);
|
|
64
|
-
process.stdout.write("----------------------------------------\n");
|
|
65
|
-
|
|
66
|
-
if (colStat.type === "numeric" && colStat.numeric) {
|
|
67
|
-
const n = colStat.numeric;
|
|
68
|
-
process.stdout.write(` count ${formatNumber(n.count)}\n`);
|
|
69
|
-
process.stdout.write(` null ${formatNumber(n.nullCount)}\n`);
|
|
70
|
-
process.stdout.write(` min ${formatDecimal(n.min)}\n`);
|
|
71
|
-
process.stdout.write(` max ${formatDecimal(n.max)}\n`);
|
|
72
|
-
process.stdout.write(` sum ${formatDecimal(n.sum)}\n`);
|
|
73
|
-
process.stdout.write(` mean ${formatDecimal(n.mean)}\n`);
|
|
74
|
-
process.stdout.write(` stddev ${formatDecimal(n.stddev)}\n`);
|
|
75
|
-
process.stdout.write(` variance ${formatDecimal(n.variance)}\n`);
|
|
76
|
-
process.stdout.write(` distinct* ~${formatNumber(n.approxDistinct)}\n\n`);
|
|
77
|
-
} else if (colStat.type === "string" && colStat.string) {
|
|
78
|
-
const s = colStat.string;
|
|
79
|
-
process.stdout.write(` count ${formatNumber(s.count)}\n`);
|
|
80
|
-
process.stdout.write(` null ${formatNumber(s.nullCount)}\n`);
|
|
81
|
-
process.stdout.write(` empty ${formatNumber(s.emptyCount)}\n`);
|
|
82
|
-
process.stdout.write(` min length ${formatNumber(s.minLength)}\n`);
|
|
83
|
-
process.stdout.write(` max length ${formatNumber(s.maxLength)}\n`);
|
|
84
|
-
process.stdout.write(` avg length ${formatDecimal(s.avgLength, 1)}\n`);
|
|
85
|
-
process.stdout.write(` distinct* ~${formatNumber(s.approxDistinct)}\n`);
|
|
86
|
-
if (s.topValues.length > 0) {
|
|
87
|
-
process.stdout.write(` top values:\n`);
|
|
88
|
-
for (const tv of s.topValues) {
|
|
89
|
-
process.stdout.write(` - "${tv.value}": ${formatNumber(tv.count)}\n`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
process.stdout.write("\n");
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
process.stdout.write("* approximate\n");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
logMemoryDebug();
|
|
100
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import {
|
|
3
|
-
type SchemaValidatorAggregator,
|
|
4
|
-
type ValidationSchemaDefinition,
|
|
5
|
-
SchemaValidatorAggregator as Validator,
|
|
6
|
-
} from "../../analytics/validator.js";
|
|
7
|
-
import { InvalidArgumentError, ValidationError } from "../../core/errors.js";
|
|
8
|
-
import { createPipeline } from "../../core/pipeline.js";
|
|
9
|
-
import { createReader, inferFormatFromPath } from "../../readers/index.js";
|
|
10
|
-
import { openReadableStream } from "../../utils/compression.js";
|
|
11
|
-
import { formatNumber, logMemoryDebug } from "../../utils/formatting.js";
|
|
12
|
-
import { ProgressReporter } from "../../utils/progress.js";
|
|
13
|
-
|
|
14
|
-
export interface ValidateCommandOptions {
|
|
15
|
-
schema: string;
|
|
16
|
-
from?: string;
|
|
17
|
-
sheet?: string;
|
|
18
|
-
path?: string;
|
|
19
|
-
delimiter?: string;
|
|
20
|
-
json?: boolean;
|
|
21
|
-
quiet?: boolean;
|
|
22
|
-
noProgress?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export async function validateCommand(
|
|
26
|
-
inputPath = "-",
|
|
27
|
-
options: ValidateCommandOptions
|
|
28
|
-
): Promise<void> {
|
|
29
|
-
if (!options.schema) {
|
|
30
|
-
throw new InvalidArgumentError("Missing required option: --schema <path_to_schema.json>");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const progress = new ProgressReporter(options);
|
|
34
|
-
|
|
35
|
-
// Read and parse schema file
|
|
36
|
-
let schemaContent: string;
|
|
37
|
-
try {
|
|
38
|
-
schemaContent = await readFile(options.schema, "utf8");
|
|
39
|
-
} catch (err) {
|
|
40
|
-
throw new InvalidArgumentError(
|
|
41
|
-
`Failed to read schema file at "${options.schema}": ${(err as Error).message}`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
let schemaDef: ValidationSchemaDefinition;
|
|
46
|
-
try {
|
|
47
|
-
schemaDef = JSON.parse(schemaContent);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
throw new InvalidArgumentError(
|
|
50
|
-
`Invalid JSON in schema file "${options.schema}": ${(err as Error).message}`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let fromFormat = options.from?.toLowerCase();
|
|
55
|
-
if (!fromFormat && inputPath !== "-") {
|
|
56
|
-
fromFormat = inferFormatFromPath(inputPath) ?? undefined;
|
|
57
|
-
}
|
|
58
|
-
if (!fromFormat) {
|
|
59
|
-
fromFormat = "csv";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const inputStream = openReadableStream(inputPath);
|
|
63
|
-
const reader = createReader(inputStream, {
|
|
64
|
-
format: fromFormat,
|
|
65
|
-
sheet: options.sheet,
|
|
66
|
-
path: options.path,
|
|
67
|
-
delimiter: options.delimiter,
|
|
68
|
-
filePath: inputPath,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const validator = new Validator(schemaDef);
|
|
72
|
-
const pipeline = createPipeline(reader);
|
|
73
|
-
pipeline.onProgress((info) => progress.update(info));
|
|
74
|
-
|
|
75
|
-
for await (const row of pipeline.rows()) {
|
|
76
|
-
validator.add(row);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
progress.done();
|
|
80
|
-
const report = validator.result();
|
|
81
|
-
|
|
82
|
-
if (options.json) {
|
|
83
|
-
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
84
|
-
} else {
|
|
85
|
-
process.stdout.write(`${formatNumber(report.totalRows)} rows scanned\n`);
|
|
86
|
-
process.stdout.write(`${formatNumber(report.validRows)} valid\n`);
|
|
87
|
-
process.stdout.write(`${formatNumber(report.invalidRows)} invalid\n`);
|
|
88
|
-
|
|
89
|
-
if (report.violations.length > 0) {
|
|
90
|
-
process.stdout.write("\nErrors\n\n");
|
|
91
|
-
const groupedByCol = new Map<string, typeof report.violations>();
|
|
92
|
-
for (const v of report.violations) {
|
|
93
|
-
if (!groupedByCol.has(v.column)) groupedByCol.set(v.column, []);
|
|
94
|
-
groupedByCol.get(v.column)!.push(v);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
for (const [col, violations] of groupedByCol.entries()) {
|
|
98
|
-
process.stdout.write(`${col}\n`);
|
|
99
|
-
for (const v of violations) {
|
|
100
|
-
process.stdout.write(` ${v.rule}\n`);
|
|
101
|
-
process.stdout.write(` ${formatNumber(v.violationsCount)} violations\n\n`);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
logMemoryDebug();
|
|
108
|
-
|
|
109
|
-
if (!report.isValid) {
|
|
110
|
-
throw new ValidationError(
|
|
111
|
-
`Schema validation failed: ${report.invalidRows} invalid rows found`,
|
|
112
|
-
report.violations.map((v) => ({
|
|
113
|
-
column: v.column,
|
|
114
|
-
expected: v.rule,
|
|
115
|
-
count: v.violationsCount,
|
|
116
|
-
examples: v.sampleInvalidValues,
|
|
117
|
-
})),
|
|
118
|
-
report.totalRows,
|
|
119
|
-
report.validRows,
|
|
120
|
-
report.invalidRows
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
}
|
package/src/cli/index.ts
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { Command } from "commander";
|
|
4
|
-
import { RowpipeError } from "../core/errors.js";
|
|
5
|
-
import { castCommand } from "./commands/cast.js";
|
|
6
|
-
import { convertCommand } from "./commands/convert.js";
|
|
7
|
-
import { filterCommand } from "./commands/filter.js";
|
|
8
|
-
import { inspectCommand } from "./commands/inspect.js";
|
|
9
|
-
import { mapCommand } from "./commands/map.js";
|
|
10
|
-
import { reduceCommand } from "./commands/reduce.js";
|
|
11
|
-
import { renameCommand } from "./commands/rename.js";
|
|
12
|
-
import { sampleCommand } from "./commands/sample.js";
|
|
13
|
-
import { schemaCommand } from "./commands/schema.js";
|
|
14
|
-
import { selectCommand } from "./commands/select.js";
|
|
15
|
-
import { statsCommand } from "./commands/stats.js";
|
|
16
|
-
import { validateCommand } from "./commands/validate.js";
|
|
17
|
-
|
|
18
|
-
// Handle broken pipe gracefully when piping to head/less
|
|
19
|
-
process.stdout.on("error", (err: unknown) => {
|
|
20
|
-
if ((err as NodeJS.ErrnoException).code === "EPIPE") {
|
|
21
|
-
process.exit(0);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const program = new Command();
|
|
26
|
-
|
|
27
|
-
program
|
|
28
|
-
.name("rowpipe")
|
|
29
|
-
.description("Stream-first tabular data toolkit for CSV, JSON, JSONL, and XLSX")
|
|
30
|
-
.version("1.0.0");
|
|
31
|
-
|
|
32
|
-
// Global options
|
|
33
|
-
program
|
|
34
|
-
.option("--batch-size <number>", "Processing batch size in rows", "1000")
|
|
35
|
-
.option("--quiet", "Suppress non-data output and progress")
|
|
36
|
-
.option("--no-progress", "Disable real-time progress bar");
|
|
37
|
-
|
|
38
|
-
// 1. inspect
|
|
39
|
-
program
|
|
40
|
-
.command("inspect [input]")
|
|
41
|
-
.description("Inspect format, row count, columns, and sheet summary")
|
|
42
|
-
.option("--sheet <sheet>", "Target worksheet name or index for XLSX")
|
|
43
|
-
.option("--path <path>", "Nested object path for JSON (e.g. data.results)")
|
|
44
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
45
|
-
.option("--json", "Output metadata as machine-readable JSON")
|
|
46
|
-
.action(async (input = "-", cmdOptions) => {
|
|
47
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
48
|
-
await inspectCommand(input, opts);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// 2. convert
|
|
52
|
-
program
|
|
53
|
-
.command("convert [input] [output]")
|
|
54
|
-
.description("Stream convert tabular datasets across formats")
|
|
55
|
-
.option("--from <format>", "Input format (csv, json, jsonl, xlsx)")
|
|
56
|
-
.option("--to <format>", "Output format (csv, json, jsonl, xlsx)")
|
|
57
|
-
.option("--sheet <sheet>", "Worksheet name or index for XLSX")
|
|
58
|
-
.option("--all-sheets", "Export all worksheets in the workbook to individual files")
|
|
59
|
-
.option("--out-dir <dir>", "Output directory for --all-sheets export")
|
|
60
|
-
.option("--path <path>", "Nested path for JSON array")
|
|
61
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
62
|
-
.option("--no-header", "Disable writing or reading headers in CSV")
|
|
63
|
-
.action(async (input = "-", output, cmdOptions) => {
|
|
64
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
65
|
-
await convertCommand(input, output, opts);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// 3. schema
|
|
69
|
-
program
|
|
70
|
-
.command("schema [input]")
|
|
71
|
-
.description("Infer column types, nullability, and semantic annotations")
|
|
72
|
-
.option("--sample <rows>", "Number of rows to sample for inference", "10000")
|
|
73
|
-
.option("--full", "Scan full stream for exact schema inference")
|
|
74
|
-
.option("--from <format>", "Input format")
|
|
75
|
-
.option("--sheet <sheet>", "Worksheet name or index for XLSX")
|
|
76
|
-
.option("--path <path>", "Nested path for JSON")
|
|
77
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
78
|
-
.option("--json", "Output schema as machine-readable JSON")
|
|
79
|
-
.action(async (input = "-", cmdOptions) => {
|
|
80
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
81
|
-
await schemaCommand(input, opts);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// 4. stats
|
|
85
|
-
program
|
|
86
|
-
.command("stats [input]")
|
|
87
|
-
.description("Compute streaming statistics (Welford numeric stats and HLL distinct counts)")
|
|
88
|
-
.option("--column <name>", "Compute statistics only for specific column")
|
|
89
|
-
.option("--fast", "Use approximate algorithms for distinct counts")
|
|
90
|
-
.option("--exact", "Use exact counts where available")
|
|
91
|
-
.option("--from <format>", "Input format")
|
|
92
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
93
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
94
|
-
.option("--json", "Output stats as machine-readable JSON")
|
|
95
|
-
.action(async (input = "-", cmdOptions) => {
|
|
96
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
97
|
-
await statsCommand(input, opts);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// 5. select
|
|
101
|
-
program
|
|
102
|
-
.command("select [input] [columns]")
|
|
103
|
-
.description("Select a subset of columns in stream")
|
|
104
|
-
.option("--from <format>", "Input format")
|
|
105
|
-
.option("--to <format>", "Output format")
|
|
106
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
107
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
108
|
-
.action(async (inputOrCols, maybeCols, cmdOptions) => {
|
|
109
|
-
let input = "-";
|
|
110
|
-
let columns = "";
|
|
111
|
-
|
|
112
|
-
if (maybeCols !== undefined) {
|
|
113
|
-
input = inputOrCols || "-";
|
|
114
|
-
columns = maybeCols;
|
|
115
|
-
} else {
|
|
116
|
-
columns = inputOrCols || "";
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
120
|
-
await selectCommand(input, columns, opts);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// 6. rename
|
|
124
|
-
program
|
|
125
|
-
.command("rename <args...>")
|
|
126
|
-
.description("Rename columns in stream (e.g. rowpipe rename users.csv old=new)")
|
|
127
|
-
.option("--from <format>", "Input format")
|
|
128
|
-
.option("--to <format>", "Output format")
|
|
129
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
130
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
131
|
-
.action(async (args, cmdOptions) => {
|
|
132
|
-
let input = "-";
|
|
133
|
-
let specs: string[] = [];
|
|
134
|
-
|
|
135
|
-
if (args[0] && !args[0].includes("=")) {
|
|
136
|
-
input = args[0];
|
|
137
|
-
specs = args.slice(1);
|
|
138
|
-
} else {
|
|
139
|
-
specs = args;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
143
|
-
await renameCommand(input, specs, opts);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// 7. cast
|
|
147
|
-
program
|
|
148
|
-
.command("cast <args...>")
|
|
149
|
-
.description("Cast column types in stream (e.g. rowpipe cast users.csv age:number active:boolean)")
|
|
150
|
-
.option("--on-error <behavior>", "Error behavior: fail, null, keep, skip-row", "null")
|
|
151
|
-
.option("--from <format>", "Input format")
|
|
152
|
-
.option("--to <format>", "Output format")
|
|
153
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
154
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
155
|
-
.action(async (args, cmdOptions) => {
|
|
156
|
-
let input = "-";
|
|
157
|
-
let specs: string[] = [];
|
|
158
|
-
|
|
159
|
-
if (args[0] && !args[0].includes(":")) {
|
|
160
|
-
input = args[0];
|
|
161
|
-
specs = args.slice(1);
|
|
162
|
-
} else {
|
|
163
|
-
specs = args;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
167
|
-
await castCommand(input, specs, opts);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
// 8. filter
|
|
171
|
-
program
|
|
172
|
-
.command("filter [input] [expression]")
|
|
173
|
-
.description("Filter rows using safe expression engine (e.g. rowpipe filter users.csv 'age > 30')")
|
|
174
|
-
.option("--from <format>", "Input format")
|
|
175
|
-
.option("--to <format>", "Output format")
|
|
176
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
177
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
178
|
-
.action(async (inputOrExpr, maybeExpr, cmdOptions) => {
|
|
179
|
-
let input = "-";
|
|
180
|
-
let expression = "";
|
|
181
|
-
|
|
182
|
-
if (maybeExpr !== undefined) {
|
|
183
|
-
input = inputOrExpr || "-";
|
|
184
|
-
expression = maybeExpr;
|
|
185
|
-
} else {
|
|
186
|
-
expression = inputOrExpr || "";
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
190
|
-
await filterCommand(input, expression, opts);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
// 9. map
|
|
194
|
-
program
|
|
195
|
-
.command("map <args...>")
|
|
196
|
-
.description("Transform and derive columns per row (e.g. rowpipe map sales.csv 'profit=revenue-cost')")
|
|
197
|
-
.option("--from <format>", "Input format")
|
|
198
|
-
.option("--to <format>", "Output format")
|
|
199
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
200
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
201
|
-
.action(async (args, cmdOptions) => {
|
|
202
|
-
let input = "-";
|
|
203
|
-
let specs: string[] = [];
|
|
204
|
-
|
|
205
|
-
if (args[0] && !args[0].includes("=")) {
|
|
206
|
-
input = args[0];
|
|
207
|
-
specs = args.slice(1);
|
|
208
|
-
} else {
|
|
209
|
-
specs = args;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
213
|
-
await mapCommand(input, specs, opts);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
// 10. reduce
|
|
217
|
-
program
|
|
218
|
-
.command("reduce <args...>")
|
|
219
|
-
.description("Aggregate and group dataset (e.g. rowpipe reduce sales.csv 'total=sum(revenue)' --by country)")
|
|
220
|
-
.option("--by <columns>", "Group by columns (comma-separated, e.g. country,category)")
|
|
221
|
-
.option("--from <format>", "Input format")
|
|
222
|
-
.option("--to <format>", "Output format")
|
|
223
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
224
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
225
|
-
.action(async (args, cmdOptions) => {
|
|
226
|
-
let input = "-";
|
|
227
|
-
let specs: string[] = [];
|
|
228
|
-
|
|
229
|
-
if (args[0] && !args[0].includes("=")) {
|
|
230
|
-
input = args[0];
|
|
231
|
-
specs = args.slice(1);
|
|
232
|
-
} else {
|
|
233
|
-
specs = args;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
237
|
-
await reduceCommand(input, specs, opts);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
// 11. sample
|
|
241
|
-
program
|
|
242
|
-
.command("sample [input]")
|
|
243
|
-
.description("Reservoir sample rows with bounded memory")
|
|
244
|
-
.option("--rows <number>", "Number of rows to sample", "1000")
|
|
245
|
-
.option("--seed <number>", "Deterministic random seed")
|
|
246
|
-
.option("--from <format>", "Input format")
|
|
247
|
-
.option("--to <format>", "Output format")
|
|
248
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
249
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
250
|
-
.action(async (input = "-", cmdOptions) => {
|
|
251
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
252
|
-
await sampleCommand(input, opts);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
// 10. validate
|
|
256
|
-
program
|
|
257
|
-
.command("validate [input]")
|
|
258
|
-
.description("Validate stream against schema definition")
|
|
259
|
-
.requiredOption("--schema <file>", "Path to JSON schema file")
|
|
260
|
-
.option("--from <format>", "Input format")
|
|
261
|
-
.option("--sheet <sheet>", "Worksheet name for XLSX")
|
|
262
|
-
.option("--delimiter <delim>", "Custom CSV delimiter")
|
|
263
|
-
.option("--json", "Output validation report as JSON")
|
|
264
|
-
.action(async (input = "-", cmdOptions) => {
|
|
265
|
-
const opts = { ...program.opts(), ...cmdOptions };
|
|
266
|
-
await validateCommand(input, opts);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
async function main() {
|
|
270
|
-
try {
|
|
271
|
-
await program.parseAsync(process.argv);
|
|
272
|
-
} catch (err: unknown) {
|
|
273
|
-
if (err instanceof RowpipeError) {
|
|
274
|
-
process.stderr.write(`\nError: ${err.message}\n`);
|
|
275
|
-
process.exit(err.exitCode);
|
|
276
|
-
}
|
|
277
|
-
process.stderr.write(`\nUnexpected error: ${(err as Error).message}\n`);
|
|
278
|
-
if (process.env["DEBUG"]) {
|
|
279
|
-
process.stderr.write(`${(err as Error).stack}\n`);
|
|
280
|
-
}
|
|
281
|
-
process.exit(1);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
main();
|