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,52 +0,0 @@
1
- import { createWriteStream } from "node:fs";
2
- import type { Writable } from "node:stream";
3
- import type { DataStream, TabularWriter, WriterOptions } from "../core/types.js";
4
-
5
- export class JSONWriter implements TabularWriter {
6
- private output: Writable | string;
7
- private createdStream?: Writable;
8
-
9
- constructor(output: Writable | string) {
10
- this.output = output;
11
- }
12
-
13
- private getOutputStream(): Writable {
14
- if (typeof this.output === "string") {
15
- this.createdStream = createWriteStream(this.output);
16
- return this.createdStream;
17
- }
18
- return this.output;
19
- }
20
-
21
- private async writeChunk(stream: Writable, data: string): Promise<void> {
22
- if (!stream.write(data)) {
23
- await new Promise<void>((resolve) => stream.once("drain", resolve));
24
- }
25
- }
26
-
27
- async write(dataStream: DataStream, _options?: WriterOptions): Promise<void> {
28
- const outStream = this.getOutputStream();
29
- let isFirstRow = true;
30
-
31
- await this.writeChunk(outStream, "[\n");
32
-
33
- for await (const batch of dataStream) {
34
- for (const row of batch.rows) {
35
- const prefix = isFirstRow ? " " : ",\n ";
36
- isFirstRow = false;
37
- await this.writeChunk(outStream, `${prefix}${JSON.stringify(row)}`);
38
- }
39
- }
40
-
41
- await this.writeChunk(outStream, "\n]\n");
42
- }
43
-
44
- async close(): Promise<void> {
45
- if (this.createdStream) {
46
- await new Promise<void>((resolve, reject) => {
47
- this.createdStream!.end(() => resolve());
48
- this.createdStream!.on("error", reject);
49
- });
50
- }
51
- }
52
- }
@@ -1,49 +0,0 @@
1
- import { createWriteStream } from "node:fs";
2
- import type { Writable } from "node:stream";
3
- import type { DataStream, TabularWriter, WriterOptions } from "../core/types.js";
4
-
5
- export class JSONLWriter implements TabularWriter {
6
- private output: Writable | string;
7
- private createdStream?: Writable;
8
-
9
- constructor(output: Writable | string) {
10
- this.output = output;
11
- }
12
-
13
- private getOutputStream(): Writable {
14
- if (typeof this.output === "string") {
15
- this.createdStream = createWriteStream(this.output);
16
- return this.createdStream;
17
- }
18
- return this.output;
19
- }
20
-
21
- private async writeChunk(stream: Writable, data: string): Promise<void> {
22
- if (!stream.write(data)) {
23
- await new Promise<void>((resolve) => stream.once("drain", resolve));
24
- }
25
- }
26
-
27
- async write(dataStream: DataStream, _options?: WriterOptions): Promise<void> {
28
- const outStream = this.getOutputStream();
29
-
30
- for await (const batch of dataStream) {
31
- let chunkText = "";
32
- for (const row of batch.rows) {
33
- chunkText += JSON.stringify(row) + "\n";
34
- }
35
- if (chunkText.length > 0) {
36
- await this.writeChunk(outStream, chunkText);
37
- }
38
- }
39
- }
40
-
41
- async close(): Promise<void> {
42
- if (this.createdStream) {
43
- await new Promise<void>((resolve, reject) => {
44
- this.createdStream!.end(() => resolve());
45
- this.createdStream!.on("error", reject);
46
- });
47
- }
48
- }
49
- }
@@ -1,87 +0,0 @@
1
- import type { Writable } from "node:stream";
2
- import writeXlsxFile from "write-excel-file/node";
3
- import type { DataStream, TabularWriter, WriterOptions } from "../core/types.js";
4
-
5
- export interface XLSXWriterOptions extends WriterOptions {
6
- sheet?: string;
7
- }
8
-
9
- export class XLSXWriter implements TabularWriter {
10
- private output: Writable | string;
11
- private options: XLSXWriterOptions;
12
-
13
- constructor(output: Writable | string, options: XLSXWriterOptions = {}) {
14
- this.output = output;
15
- this.options = { ...options };
16
- }
17
-
18
- async write(dataStream: DataStream, options?: WriterOptions): Promise<void> {
19
- const mergedOptions: XLSXWriterOptions = {
20
- ...this.options,
21
- ...options,
22
- };
23
- const sheetName = mergedOptions.sheet || "Sheet1";
24
-
25
- let headers: string[] | null = null;
26
- const grid: Array<Array<{ value?: string | number | boolean | Date; fontWeight?: string }>> = [];
27
-
28
- for await (const batch of dataStream) {
29
- if (batch.rows.length === 0) continue;
30
-
31
- if (headers === null) {
32
- const keySet = new Set<string>();
33
- for (const row of batch.rows) {
34
- for (const key of Object.keys(row)) {
35
- keySet.add(key);
36
- }
37
- }
38
- headers = Array.from(keySet);
39
-
40
- // Header row
41
- grid.push(
42
- headers.map((h) => ({
43
- value: h,
44
- fontWeight: "bold",
45
- }))
46
- );
47
- }
48
-
49
- for (const row of batch.rows) {
50
- grid.push(
51
- headers.map((h) => {
52
- const val = row[h];
53
- if (val === null || val === undefined || val === "") {
54
- return { value: undefined };
55
- }
56
- if (typeof val === "number" || typeof val === "boolean" || typeof val === "string") {
57
- return { value: val };
58
- }
59
- if (val instanceof Date) {
60
- return { value: val };
61
- }
62
- return { value: String(val) };
63
- })
64
- );
65
- }
66
- }
67
-
68
- if (grid.length === 0) {
69
- grid.push([{ value: "" }]);
70
- }
71
-
72
- const writerInstance = (writeXlsxFile as unknown as (data: unknown, opts: unknown) => {
73
- toFile: (path: string) => Promise<void>;
74
- toStream: (stream: unknown) => Promise<void>;
75
- })(grid, { sheet: sheetName });
76
-
77
- if (typeof this.output === "string") {
78
- await writerInstance.toFile(this.output);
79
- } else {
80
- await writerInstance.toStream(this.output);
81
- }
82
- }
83
-
84
- async close(): Promise<void> {
85
- // Handled on write
86
- }
87
- }
@@ -1,119 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { NumericStatsCollector, HyperLogLog, DatasetStatsAggregator } from "../src/analytics/stats.js";
3
- import { SchemaInferenceAggregator } from "../src/analytics/schema-inference.js";
4
- import { detectSemanticType } from "../src/analytics/semantic-types.js";
5
- import { SchemaValidatorAggregator } from "../src/analytics/validator.js";
6
- import type { Row } from "../src/core/types.js";
7
-
8
- describe("Welford Numeric Statistics", () => {
9
- it("should calculate exact mean, variance, stddev, sum, min, max", () => {
10
- const collector = new NumericStatsCollector();
11
- const values = [10, 20, 30, 40, 50]; // mean = 30, variance = 250, stddev = 15.811...
12
-
13
- for (const val of values) {
14
- collector.add(val);
15
- }
16
-
17
- const res = collector.result();
18
- expect(res.count).toBe(5);
19
- expect(res.nullCount).toBe(0);
20
- expect(res.min).toBe(10);
21
- expect(res.max).toBe(50);
22
- expect(res.sum).toBe(150);
23
- expect(res.mean).toBe(30);
24
- expect(res.variance).toBe(250);
25
- expect(res.stddev).toBeCloseTo(15.811388, 4);
26
- });
27
-
28
- it("should handle null and missing values properly", () => {
29
- const collector = new NumericStatsCollector();
30
- collector.add(10);
31
- collector.add(null);
32
- collector.add("");
33
- collector.add(30);
34
-
35
- const res = collector.result();
36
- expect(res.count).toBe(2);
37
- expect(res.nullCount).toBe(2);
38
- expect(res.mean).toBe(20);
39
- });
40
- });
41
-
42
- describe("HyperLogLog Distinct Estimator", () => {
43
- it("should estimate cardinality accurately with bounded memory", () => {
44
- const hll = new HyperLogLog(10);
45
- const uniqueCount = 5000;
46
-
47
- for (let i = 0; i < uniqueCount; i++) {
48
- hll.add(`user_${i}`);
49
- }
50
-
51
- const estimated = hll.count();
52
- // With p=10, error is typically within ~5%
53
- const errorRatio = Math.abs(estimated - uniqueCount) / uniqueCount;
54
- expect(errorRatio).toBeLessThan(0.08);
55
- });
56
- });
57
-
58
- describe("Schema Inference", () => {
59
- it("should infer types, nullability, and confidence", () => {
60
- const rows: Row[] = [
61
- { id: "1", name: "Alice", email: "alice@test.com", age: "30", created_at: "2026-01-01" },
62
- { id: "2", name: "Bob", email: "bob@test.com", age: "25", created_at: "2026-01-02" },
63
- { id: "3", name: "Charlie", email: null, age: "40", created_at: "2026-01-03" },
64
- ];
65
-
66
- const agg = new SchemaInferenceAggregator();
67
- for (const row of rows) agg.add(row);
68
-
69
- const schema = agg.result();
70
- const colMap = new Map(schema.columns.map((c) => [c.name, c]));
71
-
72
- expect(colMap.get("id")?.type).toBe("integer");
73
- expect(colMap.get("id")?.nullable).toBe(false);
74
-
75
- expect(colMap.get("email")?.type).toBe("string");
76
- expect(colMap.get("email")?.nullable).toBe(true);
77
- expect(colMap.get("email")?.semantic).toBe("email");
78
-
79
- expect(colMap.get("age")?.type).toBe("integer");
80
- expect(colMap.get("created_at")?.type).toBe("date");
81
- });
82
- });
83
-
84
- describe("Semantic Type Detector", () => {
85
- it("should detect emails, urls, uuids, and ips", () => {
86
- expect(detectSemanticType("user@example.com")).toBe("email");
87
- expect(detectSemanticType("https://google.com/path")).toBe("url");
88
- expect(detectSemanticType("123e4567-e89b-12d3-a456-426614174000")).toBe("uuid");
89
- expect(detectSemanticType("192.168.1.1")).toBe("ipv4");
90
- expect(detectSemanticType("USD")).toBe("currency");
91
- expect(detectSemanticType("TR")).toBe("country-code");
92
- });
93
- });
94
-
95
- describe("Schema Validator", () => {
96
- it("should validate stream and report violations", () => {
97
- const schema = {
98
- id: { type: "integer", nullable: false },
99
- email: { type: "string", nullable: false, format: "email" },
100
- age: { type: "integer", nullable: true },
101
- };
102
-
103
- const rows: Row[] = [
104
- { id: 1, email: "valid@test.com", age: 30 },
105
- { id: 2, email: null, age: 25 }, // email null violation
106
- { id: "bad_id", email: "bad@test.com", age: "not_a_num" }, // id and age type violations
107
- ];
108
-
109
- const validator = new SchemaValidatorAggregator(schema);
110
- for (const row of rows) validator.add(row);
111
-
112
- const report = validator.result();
113
- expect(report.totalRows).toBe(3);
114
- expect(report.validRows).toBe(1);
115
- expect(report.invalidRows).toBe(2);
116
- expect(report.isValid).toBe(false);
117
- expect(report.violations.length).toBeGreaterThanOrEqual(3);
118
- });
119
- });
@@ -1,187 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
- import { exec } from "node:child_process";
3
- import { promisify } from "node:util";
4
- import { promises as fs } from "node:fs";
5
- import { join } from "node:path";
6
- import { tmpdir } from "node:os";
7
- import writeXlsxFile from "write-excel-file/node";
8
-
9
- const execAsync = promisify(exec);
10
- const cliPath = join(process.cwd(), "dist/cli/index.js");
11
-
12
- describe("CLI Integration Tests", { timeout: 30000 }, () => {
13
- const tempDir = join(tmpdir(), `rowpipe_cli_test_${Date.now()}`);
14
- const sampleCsv = join(tempDir, "sample.csv");
15
- const sampleSchema = join(tempDir, "sample.schema.json");
16
- const sampleXlsx = join(tempDir, "workbook.xlsx");
17
-
18
- beforeAll(async () => {
19
- await fs.mkdir(tempDir, { recursive: true });
20
-
21
- // Build project to ensure dist is up to date
22
- await execAsync(`export PATH=$PATH:/usr/local/bin:/opt/homebrew/bin:~/.nvm/versions/node/$(ls ~/.nvm/versions/node 2>/dev/null | tail -n 1)/bin; npm run build`);
23
-
24
- // Create test CSV
25
- const csvContent = `id,name,age,email,revenue\n1,Alice,30,alice@test.com,150.50\n2,Bob,17,bob@test.com,50.00\n3,Charlie,45,charlie@test.com,500.25\n4,David,15,david@test.com,10.00\n`;
26
- await fs.writeFile(sampleCsv, csvContent, "utf8");
27
-
28
- // Create test schema
29
- const schemaContent = JSON.stringify({
30
- id: { type: "integer", nullable: false },
31
- name: { type: "string", nullable: false },
32
- age: { type: "integer", nullable: false },
33
- email: { type: "string", nullable: false, format: "email" },
34
- revenue: { type: "number", nullable: false },
35
- });
36
- await fs.writeFile(sampleSchema, schemaContent, "utf8");
37
-
38
- // Create multi-sheet XLSX workbook
39
- const usersSheet = [
40
- [{ value: "id", fontWeight: "bold" }, { value: "name", fontWeight: "bold" }, { value: "email", fontWeight: "bold" }],
41
- [{ value: 1 }, { value: "Alice" }, { value: "alice@example.com" }],
42
- [{ value: 2 }, { value: "Bob" }, { value: "bob@example.com" }],
43
- ];
44
- const ordersSheet = [
45
- [{ value: "order_id", fontWeight: "bold" }, { value: "user_id", fontWeight: "bold" }, { value: "total", fontWeight: "bold" }],
46
- [{ value: "ORD-101" }, { value: 1 }, { value: 99.9 }],
47
- [{ value: "ORD-102" }, { value: 2 }, { value: 149.5 }],
48
- [{ value: "ORD-103" }, { value: 1 }, { value: 25.0 }],
49
- ];
50
-
51
- await (writeXlsxFile as unknown as (sheets: unknown[], opts?: unknown) => { toFile: (path: string) => Promise<void> })(
52
- [
53
- { data: usersSheet, sheet: "Users" },
54
- { data: ordersSheet, sheet: "Orders" },
55
- ]
56
- ).toFile(sampleXlsx);
57
- });
58
-
59
- afterAll(async () => {
60
- await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
61
- });
62
-
63
- it("should inspect CSV file", async () => {
64
- const { stdout } = await execAsync(`node ${cliPath} inspect ${sampleCsv}`);
65
- expect(stdout).toContain("Rows: 4");
66
- expect(stdout).toContain("Columns: 5");
67
- expect(stdout).toContain("email");
68
- });
69
-
70
- it("should inspect with --json output", async () => {
71
- const { stdout } = await execAsync(`node ${cliPath} inspect ${sampleCsv} --json`);
72
- const json = JSON.parse(stdout);
73
- expect(json.rows).toBe(4);
74
- expect(json.columnsCount).toBe(5);
75
- });
76
-
77
- it("should convert CSV to JSONL", async () => {
78
- const outputJsonl = join(tempDir, "output.jsonl");
79
- await execAsync(`node ${cliPath} convert ${sampleCsv} ${outputJsonl}`);
80
-
81
- const content = await fs.readFile(outputJsonl, "utf8");
82
- const lines = content.trim().split("\n");
83
- expect(lines.length).toBe(4);
84
- expect(JSON.parse(lines[0]!).name).toBe("Alice");
85
- });
86
-
87
- it("should infer schema", async () => {
88
- const { stdout } = await execAsync(`node ${cliPath} schema ${sampleCsv}`);
89
- expect(stdout).toContain("COLUMN");
90
- expect(stdout).toContain("id");
91
- expect(stdout).toContain("integer");
92
- expect(stdout).toContain("email");
93
- });
94
-
95
- it("should calculate statistics", async () => {
96
- const { stdout } = await execAsync(`node ${cliPath} stats ${sampleCsv}`);
97
- expect(stdout).toContain("Total Rows: 4");
98
- expect(stdout).toContain("revenue (numeric)");
99
- expect(stdout).toContain("mean");
100
- });
101
-
102
- it("should filter stream", async () => {
103
- const { stdout } = await execAsync(`node ${cliPath} filter ${sampleCsv} "age >= 18"`);
104
- const lines = stdout.trim().split("\n");
105
- expect(lines.length).toBe(3);
106
- expect(stdout).toContain("Alice");
107
- expect(stdout).toContain("Charlie");
108
- expect(stdout).not.toContain("David");
109
- });
110
-
111
- it("should select columns", async () => {
112
- const { stdout } = await execAsync(`node ${cliPath} select ${sampleCsv} id,email`);
113
- const lines = stdout.trim().split("\n");
114
- expect(lines[0]).toBe("id,email");
115
- expect(lines[1]).toBe("1,alice@test.com");
116
- });
117
-
118
- it("should execute Unix pipe chain", async () => {
119
- const cmd = `cat ${sampleCsv} | node ${cliPath} filter - "age >= 18" | node ${cliPath} select - id,name,age | node ${cliPath} convert - --from csv --to jsonl`;
120
- const { stdout } = await execAsync(cmd);
121
- const lines = stdout.trim().split("\n");
122
- expect(lines.length).toBe(2);
123
-
124
- const row1 = JSON.parse(lines[0]!);
125
- expect(row1).toEqual({ id: "1", name: "Alice", age: "30" });
126
- const row2 = JSON.parse(lines[1]!);
127
- expect(row2).toEqual({ id: "3", name: "Charlie", age: "45" });
128
- });
129
-
130
- it("should validate CSV against schema", async () => {
131
- const { stdout } = await execAsync(`node ${cliPath} validate ${sampleCsv} --schema ${sampleSchema}`);
132
- expect(stdout).toContain("4 rows scanned");
133
- expect(stdout).toContain("4 valid");
134
- expect(stdout).toContain("0 invalid");
135
- });
136
-
137
- it("should inspect multi-sheet XLSX workbook", async () => {
138
- const { stdout } = await execAsync(`node ${cliPath} inspect ${sampleXlsx}`);
139
- expect(stdout).toContain("Sheets: 2");
140
- expect(stdout).toContain("Users");
141
- expect(stdout).toContain("Orders");
142
- });
143
-
144
- it("should inspect specific XLSX sheet", async () => {
145
- const { stdout } = await execAsync(`node ${cliPath} inspect ${sampleXlsx} --sheet Orders`);
146
- expect(stdout).toContain("order_id");
147
- expect(stdout).toContain("total");
148
- expect(stdout).toContain("Rows: 3");
149
- });
150
-
151
- it("should convert specific XLSX sheet to CSV", async () => {
152
- const ordersCsv = join(tempDir, "orders.csv");
153
- await execAsync(`node ${cliPath} convert ${sampleXlsx} ${ordersCsv} --sheet Orders`);
154
- const content = await fs.readFile(ordersCsv, "utf8");
155
- expect(content).toContain("order_id,user_id,total");
156
- expect(content).toContain("ORD-101");
157
- });
158
-
159
- it("should export all sheets from workbook using --all-sheets", async () => {
160
- const exportDir = join(tempDir, "all_exported");
161
- await execAsync(`node ${cliPath} convert ${sampleXlsx} --all-sheets --out-dir ${exportDir} --to csv`);
162
-
163
- const usersContent = await fs.readFile(join(exportDir, "Users.csv"), "utf8");
164
- expect(usersContent).toContain("Alice");
165
-
166
- const ordersContent = await fs.readFile(join(exportDir, "Orders.csv"), "utf8");
167
- expect(ordersContent).toContain("ORD-101");
168
- });
169
-
170
- it("should execute rowpipe map to derive new columns", async () => {
171
- const { stdout } = await execAsync(
172
- `node ${cliPath} map ${sampleCsv} "is_adult=age >= 30" "tax=revenue * 0.20"`
173
- );
174
- expect(stdout).toContain("is_adult");
175
- expect(stdout).toContain("tax");
176
- expect(stdout).toContain("30.1");
177
- });
178
-
179
- it("should execute rowpipe reduce to aggregate stream", async () => {
180
- const { stdout } = await execAsync(
181
- `node ${cliPath} reduce ${sampleCsv} "total_rev=sum(revenue)" "avg_age=avg(age)" "total=count()"`
182
- );
183
- expect(stdout).toContain("total_rev,avg_age,total");
184
- expect(stdout).toContain("710.75,26.75,4");
185
- });
186
- });
187
-