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.
Files changed (106) hide show
  1. package/README.md +25 -32
  2. package/dist/cli/index.js +4 -4
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/core/types.d.ts +2 -0
  5. package/dist/core/types.d.ts.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/readers/csv.d.ts.map +1 -1
  11. package/dist/readers/csv.js +28 -16
  12. package/dist/readers/csv.js.map +1 -1
  13. package/dist/readers/index.d.ts +1 -0
  14. package/dist/readers/index.d.ts.map +1 -1
  15. package/dist/readers/index.js +29 -3
  16. package/dist/readers/index.js.map +1 -1
  17. package/dist/readers/json.d.ts.map +1 -1
  18. package/dist/readers/json.js +6 -8
  19. package/dist/readers/json.js.map +1 -1
  20. package/dist/readers/jsonl.d.ts.map +1 -1
  21. package/dist/readers/jsonl.js +5 -7
  22. package/dist/readers/jsonl.js.map +1 -1
  23. package/dist/readers/parquet.d.ts +17 -0
  24. package/dist/readers/parquet.d.ts.map +1 -0
  25. package/dist/readers/parquet.js +146 -0
  26. package/dist/readers/parquet.js.map +1 -0
  27. package/dist/utils/compression.d.ts +30 -6
  28. package/dist/utils/compression.d.ts.map +1 -1
  29. package/dist/utils/compression.js +95 -18
  30. package/dist/utils/compression.js.map +1 -1
  31. package/dist/writers/csv.d.ts.map +1 -1
  32. package/dist/writers/csv.js +9 -11
  33. package/dist/writers/csv.js.map +1 -1
  34. package/dist/writers/index.d.ts +2 -0
  35. package/dist/writers/index.d.ts.map +1 -1
  36. package/dist/writers/index.js +49 -4
  37. package/dist/writers/index.js.map +1 -1
  38. package/dist/writers/json.d.ts +1 -1
  39. package/dist/writers/json.d.ts.map +1 -1
  40. package/dist/writers/json.js +9 -11
  41. package/dist/writers/json.js.map +1 -1
  42. package/dist/writers/jsonl.d.ts +1 -1
  43. package/dist/writers/jsonl.d.ts.map +1 -1
  44. package/dist/writers/jsonl.js +9 -11
  45. package/dist/writers/jsonl.js.map +1 -1
  46. package/dist/writers/markdown.d.ts +21 -0
  47. package/dist/writers/markdown.d.ts.map +1 -0
  48. package/dist/writers/markdown.js +82 -0
  49. package/dist/writers/markdown.js.map +1 -0
  50. package/dist/writers/parquet.d.ts +19 -0
  51. package/dist/writers/parquet.d.ts.map +1 -0
  52. package/dist/writers/parquet.js +129 -0
  53. package/dist/writers/parquet.js.map +1 -0
  54. package/package.json +13 -2
  55. package/skills/rowpipe/SKILL.md +181 -0
  56. package/benchmarks/memory-bench.ts +0 -109
  57. package/src/analytics/reduce.ts +0 -388
  58. package/src/analytics/schema-inference.ts +0 -217
  59. package/src/analytics/semantic-types.ts +0 -36
  60. package/src/analytics/stats.ts +0 -383
  61. package/src/analytics/validator.ts +0 -134
  62. package/src/cli/commands/cast.ts +0 -60
  63. package/src/cli/commands/convert.ts +0 -128
  64. package/src/cli/commands/filter.ts +0 -58
  65. package/src/cli/commands/inspect.ts +0 -193
  66. package/src/cli/commands/map.ts +0 -66
  67. package/src/cli/commands/reduce.ts +0 -74
  68. package/src/cli/commands/rename.ts +0 -59
  69. package/src/cli/commands/sample.ts +0 -61
  70. package/src/cli/commands/schema.ts +0 -86
  71. package/src/cli/commands/select.ts +0 -59
  72. package/src/cli/commands/stats.ts +0 -100
  73. package/src/cli/commands/validate.ts +0 -123
  74. package/src/cli/index.ts +0 -285
  75. package/src/core/batch.ts +0 -81
  76. package/src/core/errors.ts +0 -81
  77. package/src/core/pipeline.ts +0 -150
  78. package/src/core/types.ts +0 -110
  79. package/src/index.ts +0 -52
  80. package/src/readers/csv.ts +0 -302
  81. package/src/readers/index.ts +0 -88
  82. package/src/readers/json.ts +0 -256
  83. package/src/readers/jsonl.ts +0 -126
  84. package/src/readers/xlsx.ts +0 -182
  85. package/src/transforms/cast.ts +0 -212
  86. package/src/transforms/expression.ts +0 -1114
  87. package/src/transforms/filter.ts +0 -38
  88. package/src/transforms/map.ts +0 -99
  89. package/src/transforms/rename.ts +0 -47
  90. package/src/transforms/sample.ts +0 -57
  91. package/src/transforms/select.ts +0 -64
  92. package/src/utils/compression.ts +0 -43
  93. package/src/utils/formatting.ts +0 -75
  94. package/src/utils/progress.ts +0 -37
  95. package/src/writers/csv.ts +0 -106
  96. package/src/writers/index.ts +0 -87
  97. package/src/writers/json.ts +0 -52
  98. package/src/writers/jsonl.ts +0 -49
  99. package/src/writers/xlsx.ts +0 -87
  100. package/tests/analytics.test.ts +0 -119
  101. package/tests/cli-integration.test.ts +0 -187
  102. package/tests/map-reduce.test.ts +0 -223
  103. package/tests/readers-writers.test.ts +0 -189
  104. package/tests/transforms.test.ts +0 -199
  105. package/tsconfig.json +0 -21
  106. 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();