rowpipe 1.0.0 → 1.2.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 (185) hide show
  1. package/README.md +327 -41
  2. package/dist/cli/commands/diff.d.ts +30 -0
  3. package/dist/cli/commands/diff.d.ts.map +1 -0
  4. package/dist/cli/commands/diff.js +143 -0
  5. package/dist/cli/commands/diff.js.map +1 -0
  6. package/dist/cli/commands/files.d.ts +35 -0
  7. package/dist/cli/commands/files.d.ts.map +1 -0
  8. package/dist/cli/commands/files.js +93 -0
  9. package/dist/cli/commands/files.js.map +1 -0
  10. package/dist/cli/index.js +89 -5
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/core/errors.d.ts +23 -0
  13. package/dist/core/errors.d.ts.map +1 -1
  14. package/dist/core/errors.js +38 -0
  15. package/dist/core/errors.js.map +1 -1
  16. package/dist/core/types.d.ts +4 -0
  17. package/dist/core/types.d.ts.map +1 -1
  18. package/dist/diff/comparator.d.ts +17 -0
  19. package/dist/diff/comparator.d.ts.map +1 -0
  20. package/dist/diff/comparator.js +158 -0
  21. package/dist/diff/comparator.js.map +1 -0
  22. package/dist/diff/engine.d.ts +10 -0
  23. package/dist/diff/engine.d.ts.map +1 -0
  24. package/dist/diff/engine.js +333 -0
  25. package/dist/diff/engine.js.map +1 -0
  26. package/dist/diff/hash.d.ts +6 -0
  27. package/dist/diff/hash.d.ts.map +1 -0
  28. package/dist/diff/hash.js +50 -0
  29. package/dist/diff/hash.js.map +1 -0
  30. package/dist/diff/key.d.ts +21 -0
  31. package/dist/diff/key.d.ts.map +1 -0
  32. package/dist/diff/key.js +107 -0
  33. package/dist/diff/key.js.map +1 -0
  34. package/dist/diff/reporter.d.ts +18 -0
  35. package/dist/diff/reporter.d.ts.map +1 -0
  36. package/dist/diff/reporter.js +133 -0
  37. package/dist/diff/reporter.js.map +1 -0
  38. package/dist/diff/schema.d.ts +7 -0
  39. package/dist/diff/schema.d.ts.map +1 -0
  40. package/dist/diff/schema.js +65 -0
  41. package/dist/diff/schema.js.map +1 -0
  42. package/dist/diff/storage/disk-index.d.ts +28 -0
  43. package/dist/diff/storage/disk-index.d.ts.map +1 -0
  44. package/dist/diff/storage/disk-index.js +105 -0
  45. package/dist/diff/storage/disk-index.js.map +1 -0
  46. package/dist/diff/storage/memory-index.d.ts +23 -0
  47. package/dist/diff/storage/memory-index.d.ts.map +1 -0
  48. package/dist/diff/storage/memory-index.js +69 -0
  49. package/dist/diff/storage/memory-index.js.map +1 -0
  50. package/dist/diff/storage/spillable-index.d.ts +30 -0
  51. package/dist/diff/storage/spillable-index.d.ts.map +1 -0
  52. package/dist/diff/storage/spillable-index.js +108 -0
  53. package/dist/diff/storage/spillable-index.js.map +1 -0
  54. package/dist/diff/types.d.ts +127 -0
  55. package/dist/diff/types.d.ts.map +1 -0
  56. package/dist/diff/types.js +2 -0
  57. package/dist/diff/types.js.map +1 -0
  58. package/dist/files/glob.d.ts +21 -0
  59. package/dist/files/glob.d.ts.map +1 -0
  60. package/dist/files/glob.js +132 -0
  61. package/dist/files/glob.js.map +1 -0
  62. package/dist/files/hash.d.ts +10 -0
  63. package/dist/files/hash.d.ts.map +1 -0
  64. package/dist/files/hash.js +67 -0
  65. package/dist/files/hash.js.map +1 -0
  66. package/dist/files/index.d.ts +6 -0
  67. package/dist/files/index.d.ts.map +1 -0
  68. package/dist/files/index.js +6 -0
  69. package/dist/files/index.js.map +1 -0
  70. package/dist/files/mime.d.ts +9 -0
  71. package/dist/files/mime.d.ts.map +1 -0
  72. package/dist/files/mime.js +128 -0
  73. package/dist/files/mime.js.map +1 -0
  74. package/dist/files/reader.d.ts +19 -0
  75. package/dist/files/reader.d.ts.map +1 -0
  76. package/dist/files/reader.js +344 -0
  77. package/dist/files/reader.js.map +1 -0
  78. package/dist/files/types.d.ts +47 -0
  79. package/dist/files/types.d.ts.map +1 -0
  80. package/dist/files/types.js +2 -0
  81. package/dist/files/types.js.map +1 -0
  82. package/dist/index.d.ts +17 -2
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +19 -2
  85. package/dist/index.js.map +1 -1
  86. package/dist/readers/csv.d.ts.map +1 -1
  87. package/dist/readers/csv.js +59 -27
  88. package/dist/readers/csv.js.map +1 -1
  89. package/dist/readers/index.d.ts +2 -0
  90. package/dist/readers/index.d.ts.map +1 -1
  91. package/dist/readers/index.js +57 -3
  92. package/dist/readers/index.js.map +1 -1
  93. package/dist/readers/json.d.ts.map +1 -1
  94. package/dist/readers/json.js +6 -8
  95. package/dist/readers/json.js.map +1 -1
  96. package/dist/readers/jsonl.d.ts.map +1 -1
  97. package/dist/readers/jsonl.js +5 -7
  98. package/dist/readers/jsonl.js.map +1 -1
  99. package/dist/readers/parquet.d.ts +17 -0
  100. package/dist/readers/parquet.d.ts.map +1 -0
  101. package/dist/readers/parquet.js +146 -0
  102. package/dist/readers/parquet.js.map +1 -0
  103. package/dist/transforms/expression.d.ts.map +1 -1
  104. package/dist/transforms/expression.js +73 -7
  105. package/dist/transforms/expression.js.map +1 -1
  106. package/dist/utils/compression.d.ts +30 -6
  107. package/dist/utils/compression.d.ts.map +1 -1
  108. package/dist/utils/compression.js +95 -18
  109. package/dist/utils/compression.js.map +1 -1
  110. package/dist/writers/csv.d.ts.map +1 -1
  111. package/dist/writers/csv.js +29 -17
  112. package/dist/writers/csv.js.map +1 -1
  113. package/dist/writers/index.d.ts +2 -0
  114. package/dist/writers/index.d.ts.map +1 -1
  115. package/dist/writers/index.js +49 -4
  116. package/dist/writers/index.js.map +1 -1
  117. package/dist/writers/json.d.ts +1 -1
  118. package/dist/writers/json.d.ts.map +1 -1
  119. package/dist/writers/json.js +9 -11
  120. package/dist/writers/json.js.map +1 -1
  121. package/dist/writers/jsonl.d.ts +1 -1
  122. package/dist/writers/jsonl.d.ts.map +1 -1
  123. package/dist/writers/jsonl.js +9 -11
  124. package/dist/writers/jsonl.js.map +1 -1
  125. package/dist/writers/markdown.d.ts +21 -0
  126. package/dist/writers/markdown.d.ts.map +1 -0
  127. package/dist/writers/markdown.js +82 -0
  128. package/dist/writers/markdown.js.map +1 -0
  129. package/dist/writers/parquet.d.ts +19 -0
  130. package/dist/writers/parquet.d.ts.map +1 -0
  131. package/dist/writers/parquet.js +129 -0
  132. package/dist/writers/parquet.js.map +1 -0
  133. package/package.json +16 -3
  134. package/skills/rowpipe/SKILL.md +263 -0
  135. package/benchmarks/memory-bench.ts +0 -109
  136. package/src/analytics/reduce.ts +0 -388
  137. package/src/analytics/schema-inference.ts +0 -217
  138. package/src/analytics/semantic-types.ts +0 -36
  139. package/src/analytics/stats.ts +0 -383
  140. package/src/analytics/validator.ts +0 -134
  141. package/src/cli/commands/cast.ts +0 -60
  142. package/src/cli/commands/convert.ts +0 -128
  143. package/src/cli/commands/filter.ts +0 -58
  144. package/src/cli/commands/inspect.ts +0 -193
  145. package/src/cli/commands/map.ts +0 -66
  146. package/src/cli/commands/reduce.ts +0 -74
  147. package/src/cli/commands/rename.ts +0 -59
  148. package/src/cli/commands/sample.ts +0 -61
  149. package/src/cli/commands/schema.ts +0 -86
  150. package/src/cli/commands/select.ts +0 -59
  151. package/src/cli/commands/stats.ts +0 -100
  152. package/src/cli/commands/validate.ts +0 -123
  153. package/src/cli/index.ts +0 -285
  154. package/src/core/batch.ts +0 -81
  155. package/src/core/errors.ts +0 -81
  156. package/src/core/pipeline.ts +0 -150
  157. package/src/core/types.ts +0 -110
  158. package/src/index.ts +0 -52
  159. package/src/readers/csv.ts +0 -302
  160. package/src/readers/index.ts +0 -88
  161. package/src/readers/json.ts +0 -256
  162. package/src/readers/jsonl.ts +0 -126
  163. package/src/readers/xlsx.ts +0 -182
  164. package/src/transforms/cast.ts +0 -212
  165. package/src/transforms/expression.ts +0 -1114
  166. package/src/transforms/filter.ts +0 -38
  167. package/src/transforms/map.ts +0 -99
  168. package/src/transforms/rename.ts +0 -47
  169. package/src/transforms/sample.ts +0 -57
  170. package/src/transforms/select.ts +0 -64
  171. package/src/utils/compression.ts +0 -43
  172. package/src/utils/formatting.ts +0 -75
  173. package/src/utils/progress.ts +0 -37
  174. package/src/writers/csv.ts +0 -106
  175. package/src/writers/index.ts +0 -87
  176. package/src/writers/json.ts +0 -52
  177. package/src/writers/jsonl.ts +0 -49
  178. package/src/writers/xlsx.ts +0 -87
  179. package/tests/analytics.test.ts +0 -119
  180. package/tests/cli-integration.test.ts +0 -187
  181. package/tests/map-reduce.test.ts +0 -223
  182. package/tests/readers-writers.test.ts +0 -189
  183. package/tests/transforms.test.ts +0 -199
  184. package/tsconfig.json +0 -21
  185. package/vitest.config.ts +0 -9
@@ -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();
package/src/core/batch.ts DELETED
@@ -1,81 +0,0 @@
1
- import type { DataBatch, DataStream, Row } from "./types.js";
2
-
3
- /**
4
- * Packs an AsyncIterable of individual rows into DataBatch chunks with a given batchSize.
5
- */
6
- export async function* rowsToBatches(
7
- rows: AsyncIterable<Row>,
8
- batchSize = 1000
9
- ): AsyncGenerator<DataBatch> {
10
- const effectiveBatchSize = Math.max(1, batchSize);
11
- let currentRows: Row[] = [];
12
- let totalOffset = 0;
13
-
14
- for await (const row of rows) {
15
- currentRows.push(row);
16
- if (currentRows.length >= effectiveBatchSize) {
17
- yield {
18
- rows: currentRows,
19
- offset: totalOffset,
20
- };
21
- totalOffset += currentRows.length;
22
- currentRows = [];
23
- }
24
- }
25
-
26
- if (currentRows.length > 0) {
27
- yield {
28
- rows: currentRows,
29
- offset: totalOffset,
30
- };
31
- }
32
- }
33
-
34
- /**
35
- * Unpacks DataBatches into individual rows.
36
- */
37
- export async function* batchesToRows(stream: DataStream): AsyncGenerator<Row> {
38
- for await (const batch of stream) {
39
- for (const row of batch.rows) {
40
- yield row;
41
- }
42
- }
43
- }
44
-
45
- /**
46
- * Applies a mapping function to each row in batches, maintaining batch structure
47
- * and filtering out null/undefined results.
48
- */
49
- export async function* batchMap(
50
- stream: DataStream,
51
- fn: (row: Row, globalIndex: number) => Row | null | undefined,
52
- targetBatchSize = 1000
53
- ): AsyncGenerator<DataBatch> {
54
- let pendingRows: Row[] = [];
55
- let currentOffset = 0;
56
- let globalIndex = 0;
57
-
58
- for await (const batch of stream) {
59
- for (const row of batch.rows) {
60
- const transformed = fn(row, globalIndex++);
61
- if (transformed !== null && transformed !== undefined) {
62
- pendingRows.push(transformed);
63
- if (pendingRows.length >= targetBatchSize) {
64
- yield {
65
- rows: pendingRows,
66
- offset: currentOffset,
67
- };
68
- currentOffset += pendingRows.length;
69
- pendingRows = [];
70
- }
71
- }
72
- }
73
- }
74
-
75
- if (pendingRows.length > 0) {
76
- yield {
77
- rows: pendingRows,
78
- offset: currentOffset,
79
- };
80
- }
81
- }
@@ -1,81 +0,0 @@
1
- export class RowpipeError extends Error {
2
- public readonly exitCode: number;
3
-
4
- constructor(message: string, exitCode = 1) {
5
- super(message);
6
- this.name = "RowpipeError";
7
- this.exitCode = exitCode;
8
- Object.setPrototypeOf(this, new.target.prototype);
9
- }
10
- }
11
-
12
- export class InvalidArgumentError extends RowpipeError {
13
- constructor(message: string) {
14
- super(message, 2);
15
- this.name = "InvalidArgumentError";
16
- }
17
- }
18
-
19
- export interface ParseErrorContext {
20
- file?: string;
21
- sheet?: string;
22
- row?: number;
23
- column?: string | number;
24
- byteOffset?: number;
25
- line?: number;
26
- cell?: string;
27
- }
28
-
29
- export class ParseError extends RowpipeError {
30
- public readonly context: ParseErrorContext;
31
-
32
- constructor(message: string, context: ParseErrorContext = {}) {
33
- let detailedMsg = `Parse error: ${message}`;
34
- const parts: string[] = [];
35
-
36
- if (context.file) parts.push(`File: ${context.file}`);
37
- if (context.sheet) parts.push(`Sheet: ${context.sheet}`);
38
- if (context.row !== undefined) parts.push(`Row: ${context.row}`);
39
- if (context.column !== undefined) parts.push(`Column: ${context.column}`);
40
- if (context.cell) parts.push(`Cell: ${context.cell}`);
41
- if (context.byteOffset !== undefined) parts.push(`Byte offset: ${context.byteOffset}`);
42
-
43
- if (parts.length > 0) {
44
- detailedMsg += `\n Context: ${parts.join(", ")}`;
45
- }
46
-
47
- super(detailedMsg, 3);
48
- this.name = "ParseError";
49
- this.context = context;
50
- }
51
- }
52
-
53
- export interface ValidationViolation {
54
- column: string;
55
- expected: string;
56
- actual?: string;
57
- count: number;
58
- examples?: unknown[];
59
- }
60
-
61
- export class ValidationError extends RowpipeError {
62
- public readonly violations: ValidationViolation[];
63
- public readonly totalRows: number;
64
- public readonly validRows: number;
65
- public readonly invalidRows: number;
66
-
67
- constructor(
68
- message: string,
69
- violations: ValidationViolation[] = [],
70
- totalRows = 0,
71
- validRows = 0,
72
- invalidRows = 0
73
- ) {
74
- super(message, 4);
75
- this.name = "ValidationError";
76
- this.violations = violations;
77
- this.totalRows = totalRows;
78
- this.validRows = validRows;
79
- this.invalidRows = invalidRows;
80
- }
81
- }