parquetlens 0.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.
@@ -0,0 +1,92 @@
1
+ // ../../packages/parquet-reader/dist/index.js
2
+ import { Blob as NodeBlob } from "buffer";
3
+ import { createWriteStream } from "fs";
4
+ import { promises as fs } from "fs";
5
+ import { randomUUID } from "crypto";
6
+ import { tmpdir } from "os";
7
+ import path from "path";
8
+ import { pipeline } from "stream/promises";
9
+ import { tableFromIPC } from "apache-arrow";
10
+ import { ParquetFile, readParquet } from "parquet-wasm/node";
11
+ var BlobCtor = typeof Blob === "undefined" ? NodeBlob : Blob;
12
+ function readParquetTableFromBuffer(buffer, options) {
13
+ const wasmTable = readParquet(buffer, options ?? void 0);
14
+ const ipcStream = wasmTable.intoIPCStream();
15
+ return tableFromIPC(ipcStream);
16
+ }
17
+ function createParquetBufferSource(buffer) {
18
+ let metadataPromise = null;
19
+ return {
20
+ buffer,
21
+ byteLength: buffer.byteLength,
22
+ readTable: (options) => readParquetTableFromBuffer(buffer, options),
23
+ readMetadata: () => {
24
+ if (!metadataPromise) {
25
+ metadataPromise = readParquetMetadataFromBuffer(buffer);
26
+ }
27
+ return metadataPromise;
28
+ }
29
+ };
30
+ }
31
+ async function openParquetBufferFromPath(filePath) {
32
+ const buffer = await fs.readFile(filePath);
33
+ return createParquetBufferSource(buffer);
34
+ }
35
+ async function readParquetTableFromPath(filePath, options) {
36
+ const buffer = await fs.readFile(filePath);
37
+ return readParquetTableFromBuffer(buffer, options);
38
+ }
39
+ async function readParquetMetadataFromBuffer(buffer) {
40
+ const blobInput = new Uint8Array(buffer).buffer;
41
+ const file = await ParquetFile.fromFile(new BlobCtor([blobInput]));
42
+ const meta = file.metadata();
43
+ const fileMeta = meta.fileMetadata();
44
+ const createdBy = fileMeta.createdBy();
45
+ const keyValueMetadata = Object.fromEntries(fileMeta.keyValueMetadata());
46
+ fileMeta.free();
47
+ meta.free();
48
+ file.free();
49
+ return {
50
+ createdBy: createdBy ?? void 0,
51
+ keyValueMetadata: normalizeMetadataValues(keyValueMetadata)
52
+ };
53
+ }
54
+ function normalizeMetadataValues(input) {
55
+ const normalized = {};
56
+ for (const [key, value] of Object.entries(input)) {
57
+ if (value === null || value === void 0) {
58
+ normalized[key] = "";
59
+ continue;
60
+ }
61
+ normalized[key] = typeof value === "string" ? value : String(value);
62
+ }
63
+ return normalized;
64
+ }
65
+ async function bufferStdinToTempFile(filenameHint = "stdin.parquet") {
66
+ const tempDir = await fs.mkdtemp(path.join(tmpdir(), "parquetlens-"));
67
+ const safeName = filenameHint.replace(/[\\/]/g, "_");
68
+ const filePath = path.join(tempDir, `${randomUUID()}-${safeName}`);
69
+ const writeStream = createWriteStream(filePath);
70
+ await pipeline(process.stdin, writeStream);
71
+ return {
72
+ path: filePath,
73
+ cleanup: async () => {
74
+ await fs.rm(tempDir, { recursive: true, force: true });
75
+ }
76
+ };
77
+ }
78
+ async function readParquetTableFromStdin(filenameHint = "stdin.parquet", options) {
79
+ const temp = await bufferStdinToTempFile(filenameHint);
80
+ try {
81
+ return await readParquetTableFromPath(temp.path, options);
82
+ } finally {
83
+ await temp.cleanup();
84
+ }
85
+ }
86
+
87
+ export {
88
+ openParquetBufferFromPath,
89
+ readParquetTableFromPath,
90
+ readParquetTableFromStdin
91
+ };
92
+ //# sourceMappingURL=chunk-VFBGUOAH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../packages/parquet-reader/src/index.ts"],"sourcesContent":["import { Blob as NodeBlob } from \"node:buffer\";\nimport { createWriteStream } from \"node:fs\";\nimport { promises as fs } from \"node:fs\";\nimport { randomUUID } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\n\nimport { tableFromIPC, Table } from \"apache-arrow\";\nimport { ParquetFile, readParquet, ReaderOptions } from \"parquet-wasm/node\";\n\nconst BlobCtor: typeof Blob =\n typeof Blob === \"undefined\" ? (NodeBlob as unknown as typeof Blob) : Blob;\n\nexport type TempParquetFile = {\n path: string;\n cleanup: () => Promise<void>;\n};\n\nexport type ParquetReadOptions = Pick<\n ReaderOptions,\n \"batchSize\" | \"columns\" | \"limit\" | \"offset\" | \"rowGroups\"\n>;\n\nexport type ParquetFileMetadata = {\n createdBy?: string;\n keyValueMetadata: Record<string, string>;\n};\n\nexport type ParquetBufferSource = {\n buffer: Uint8Array;\n byteLength: number;\n readTable: (options?: ParquetReadOptions) => Table;\n readMetadata: () => Promise<ParquetFileMetadata>;\n};\n\nexport function readParquetTableFromBuffer(\n buffer: Uint8Array,\n options?: ParquetReadOptions,\n): Table {\n const wasmTable = readParquet(buffer, options ?? undefined);\n const ipcStream = wasmTable.intoIPCStream();\n return tableFromIPC(ipcStream);\n}\n\nexport function createParquetBufferSource(buffer: Uint8Array): ParquetBufferSource {\n let metadataPromise: Promise<ParquetFileMetadata> | null = null;\n\n return {\n buffer,\n byteLength: buffer.byteLength,\n readTable: (options?: ParquetReadOptions) => readParquetTableFromBuffer(buffer, options),\n readMetadata: () => {\n if (!metadataPromise) {\n metadataPromise = readParquetMetadataFromBuffer(buffer);\n }\n return metadataPromise;\n },\n };\n}\n\nexport async function openParquetBufferFromPath(filePath: string): Promise<ParquetBufferSource> {\n const buffer = await fs.readFile(filePath);\n return createParquetBufferSource(buffer);\n}\n\nexport async function readParquetTableFromPath(\n filePath: string,\n options?: ParquetReadOptions,\n): Promise<Table> {\n const buffer = await fs.readFile(filePath);\n return readParquetTableFromBuffer(buffer, options);\n}\n\nexport async function readParquetMetadataFromBuffer(\n buffer: Uint8Array,\n): Promise<ParquetFileMetadata> {\n const blobInput = new Uint8Array(buffer).buffer as ArrayBuffer;\n const file = await ParquetFile.fromFile(new BlobCtor([blobInput]));\n const meta = file.metadata();\n const fileMeta = meta.fileMetadata();\n const createdBy = fileMeta.createdBy();\n const keyValueMetadata = Object.fromEntries(fileMeta.keyValueMetadata());\n\n fileMeta.free();\n meta.free();\n file.free();\n\n return {\n createdBy: createdBy ?? undefined,\n keyValueMetadata: normalizeMetadataValues(keyValueMetadata),\n };\n}\n\nfunction normalizeMetadataValues(input: Record<string, unknown>): Record<string, string> {\n const normalized: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(input)) {\n if (value === null || value === undefined) {\n normalized[key] = \"\";\n continue;\n }\n normalized[key] = typeof value === \"string\" ? value : String(value);\n }\n\n return normalized;\n}\n\nexport async function bufferStdinToTempFile(\n filenameHint = \"stdin.parquet\",\n): Promise<TempParquetFile> {\n const tempDir = await fs.mkdtemp(path.join(tmpdir(), \"parquetlens-\"));\n const safeName = filenameHint.replace(/[\\\\/]/g, \"_\");\n const filePath = path.join(tempDir, `${randomUUID()}-${safeName}`);\n const writeStream = createWriteStream(filePath);\n\n await pipeline(process.stdin, writeStream);\n\n return {\n path: filePath,\n cleanup: async () => {\n await fs.rm(tempDir, { recursive: true, force: true });\n },\n };\n}\n\nexport async function readParquetTableFromStdin(\n filenameHint = \"stdin.parquet\",\n options?: ParquetReadOptions,\n): Promise<Table> {\n const temp = await bufferStdinToTempFile(filenameHint);\n\n try {\n return await readParquetTableFromPath(temp.path, options);\n } finally {\n await temp.cleanup();\n }\n}\n"],"mappings":";AAAA,SAAS,QAAQ,gBAAgB;AACjC,SAAS,yBAAyB;AAClC,SAAS,YAAY,UAAU;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,OAAO,UAAU;AACjB,SAAS,gBAAgB;AAEzB,SAAS,oBAA2B;AACpC,SAAS,aAAa,mBAAkC;AAExD,IAAM,WACJ,OAAO,SAAS,cAAe,WAAsC;AAwBhE,SAAS,2BACd,QACA,SACO;AACP,QAAM,YAAY,YAAY,QAAQ,WAAW,MAAS;AAC1D,QAAM,YAAY,UAAU,cAAc;AAC1C,SAAO,aAAa,SAAS;AAC/B;AAEO,SAAS,0BAA0B,QAAyC;AACjF,MAAI,kBAAuD;AAE3D,SAAO;IACL;IACA,YAAY,OAAO;IACnB,WAAW,CAAC,YAAiC,2BAA2B,QAAQ,OAAO;IACvF,cAAc,MAAM;AAClB,UAAI,CAAC,iBAAiB;AACpB,0BAAkB,8BAA8B,MAAM;MACxD;AACA,aAAO;IACT;EACF;AACF;AAEA,eAAsB,0BAA0B,UAAgD;AAC9F,QAAM,SAAS,MAAM,GAAG,SAAS,QAAQ;AACzC,SAAO,0BAA0B,MAAM;AACzC;AAEA,eAAsB,yBACpB,UACA,SACgB;AAChB,QAAM,SAAS,MAAM,GAAG,SAAS,QAAQ;AACzC,SAAO,2BAA2B,QAAQ,OAAO;AACnD;AAEA,eAAsB,8BACpB,QAC8B;AAC9B,QAAM,YAAY,IAAI,WAAW,MAAM,EAAE;AACzC,QAAM,OAAO,MAAM,YAAY,SAAS,IAAI,SAAS,CAAC,SAAS,CAAC,CAAC;AACjE,QAAM,OAAO,KAAK,SAAS;AAC3B,QAAM,WAAW,KAAK,aAAa;AACnC,QAAM,YAAY,SAAS,UAAU;AACrC,QAAM,mBAAmB,OAAO,YAAY,SAAS,iBAAiB,CAAC;AAEvE,WAAS,KAAK;AACd,OAAK,KAAK;AACV,OAAK,KAAK;AAEV,SAAO;IACL,WAAW,aAAa;IACxB,kBAAkB,wBAAwB,gBAAgB;EAC5D;AACF;AAEA,SAAS,wBAAwB,OAAwD;AACvF,QAAM,aAAqC,CAAC;AAE5C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,iBAAW,GAAG,IAAI;AAClB;IACF;AACA,eAAW,GAAG,IAAI,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;EACpE;AAEA,SAAO;AACT;AAEA,eAAsB,sBACpB,eAAe,iBACW;AAC1B,QAAM,UAAU,MAAM,GAAG,QAAQ,KAAK,KAAK,OAAO,GAAG,cAAc,CAAC;AACpE,QAAM,WAAW,aAAa,QAAQ,UAAU,GAAG;AACnD,QAAM,WAAW,KAAK,KAAK,SAAS,GAAG,WAAW,CAAC,IAAI,QAAQ,EAAE;AACjE,QAAM,cAAc,kBAAkB,QAAQ;AAE9C,QAAM,SAAS,QAAQ,OAAO,WAAW;AAEzC,SAAO;IACL,MAAM;IACN,SAAS,YAAY;AACnB,YAAM,GAAG,GAAG,SAAS,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;IACvD;EACF;AACF;AAEA,eAAsB,0BACpB,eAAe,iBACf,SACgB;AAChB,QAAM,OAAO,MAAM,sBAAsB,YAAY;AAErD,MAAI;AACF,WAAO,MAAM,yBAAyB,KAAK,MAAM,OAAO;EAC1D,UAAA;AACE,UAAM,KAAK,QAAQ;EACrB;AACF;","names":[]}
package/dist/main.js ADDED
@@ -0,0 +1,317 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname } from 'path';
5
+ const require = createRequire(import.meta.url);
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ import {
9
+ readParquetTableFromPath,
10
+ readParquetTableFromStdin
11
+ } from "./chunk-573AA4JN.js";
12
+
13
+ // src/main.ts
14
+ import { spawnSync } from "child_process";
15
+ import path from "path";
16
+ var DEFAULT_LIMIT = 20;
17
+ function parseArgs(argv) {
18
+ const options = {
19
+ limit: DEFAULT_LIMIT,
20
+ columns: [],
21
+ json: false,
22
+ schemaOnly: false,
23
+ showSchema: true,
24
+ tuiMode: "auto"
25
+ };
26
+ let input;
27
+ let limitSpecified = false;
28
+ for (let i = 0; i < argv.length; i += 1) {
29
+ const arg = argv[i];
30
+ if (arg === "--") {
31
+ continue;
32
+ }
33
+ if (arg === "-h" || arg === "--help") {
34
+ return { options, limitSpecified, help: true };
35
+ }
36
+ if (arg === "--json") {
37
+ options.json = true;
38
+ options.tuiMode = "off";
39
+ continue;
40
+ }
41
+ if (arg === "--tui") {
42
+ options.tuiMode = "on";
43
+ continue;
44
+ }
45
+ if (arg === "--plain" || arg === "--no-tui") {
46
+ options.tuiMode = "off";
47
+ continue;
48
+ }
49
+ if (arg === "--schema") {
50
+ options.schemaOnly = true;
51
+ options.tuiMode = "off";
52
+ continue;
53
+ }
54
+ if (arg === "--no-schema") {
55
+ options.showSchema = false;
56
+ continue;
57
+ }
58
+ const limitValue = readOptionValue(arg, "--limit", argv[i + 1]);
59
+ if (limitValue) {
60
+ const parsed = Number.parseInt(limitValue.value, 10);
61
+ if (!Number.isFinite(parsed) || parsed < 0) {
62
+ return {
63
+ options,
64
+ limitSpecified,
65
+ help: false,
66
+ error: `invalid --limit value: ${limitValue.value}`
67
+ };
68
+ }
69
+ options.limit = parsed;
70
+ limitSpecified = true;
71
+ if (limitValue.usedNext) {
72
+ i += 1;
73
+ }
74
+ continue;
75
+ }
76
+ const columnsValue = readOptionValue(arg, "--columns", argv[i + 1]);
77
+ if (columnsValue) {
78
+ const rawColumns = columnsValue.value.split(",").map((value) => value.trim()).filter(Boolean);
79
+ options.columns = rawColumns;
80
+ if (columnsValue.usedNext) {
81
+ i += 1;
82
+ }
83
+ continue;
84
+ }
85
+ if (arg === "-") {
86
+ if (input) {
87
+ return { options, limitSpecified, help: false, error: "unexpected extra argument: -" };
88
+ }
89
+ input = arg;
90
+ continue;
91
+ }
92
+ if (arg.startsWith("-")) {
93
+ return { options, limitSpecified, help: false, error: `unknown option: ${arg}` };
94
+ }
95
+ if (input) {
96
+ return { options, limitSpecified, help: false, error: `unexpected extra argument: ${arg}` };
97
+ }
98
+ input = arg;
99
+ }
100
+ return { input, options, limitSpecified, help: false };
101
+ }
102
+ function readOptionValue(arg, name, next) {
103
+ if (arg === name) {
104
+ if (!next || next.startsWith("-")) {
105
+ return null;
106
+ }
107
+ return { value: next, usedNext: true };
108
+ }
109
+ if (arg.startsWith(`${name}=`)) {
110
+ return { value: arg.slice(name.length + 1), usedNext: false };
111
+ }
112
+ return null;
113
+ }
114
+ function printUsage() {
115
+ const helpText = `parquetlens <file|-> [options]
116
+
117
+ options:
118
+ --limit, --limit=<n> number of rows to show (default: ${DEFAULT_LIMIT})
119
+ --columns, --columns=<c> comma-separated column list
120
+ --schema print schema only
121
+ --no-schema skip schema output
122
+ --json output rows as json lines
123
+ --tui open interactive viewer (default)
124
+ --plain, --no-tui disable interactive viewer
125
+ -h, --help show help
126
+
127
+ examples:
128
+ parquetlens data.parquet --limit 25
129
+ parquetlens data.parquet --columns=city,state
130
+ parquetlens data.parquet --tui
131
+ parquetlens data.parquet --plain
132
+ parquetlens - < input.parquet
133
+ `;
134
+ process.stdout.write(helpText);
135
+ }
136
+ function formatSchema(table) {
137
+ const lines = table.schema.fields.map((field, index) => {
138
+ const typeName = String(field.type);
139
+ return `${index + 1}. ${field.name}: ${typeName}`;
140
+ });
141
+ return lines.join("\n");
142
+ }
143
+ function totalRows(table) {
144
+ return table.batches.reduce((count, batch) => count + batch.numRows, 0);
145
+ }
146
+ function resolveColumns(table, requested) {
147
+ const fields = table.schema.fields;
148
+ const nameToIndex = /* @__PURE__ */ new Map();
149
+ fields.forEach((field, index) => {
150
+ nameToIndex.set(field.name, index);
151
+ });
152
+ const names = requested.length > 0 ? requested : fields.map((field) => field.name);
153
+ const missing = names.filter((name) => !nameToIndex.has(name));
154
+ if (missing.length > 0) {
155
+ throw new Error(`unknown columns: ${missing.join(", ")}`);
156
+ }
157
+ return {
158
+ names,
159
+ indices: names.map((name) => nameToIndex.get(name) ?? -1)
160
+ };
161
+ }
162
+ function formatCell(value) {
163
+ if (value === null || value === void 0) {
164
+ return null;
165
+ }
166
+ if (typeof value === "bigint") {
167
+ return value.toString();
168
+ }
169
+ if (value instanceof Date) {
170
+ return value.toISOString();
171
+ }
172
+ if (value instanceof Uint8Array) {
173
+ return `Uint8Array(${value.length})`;
174
+ }
175
+ if (typeof value === "object") {
176
+ try {
177
+ return JSON.stringify(value);
178
+ } catch {
179
+ return String(value);
180
+ }
181
+ }
182
+ return value;
183
+ }
184
+ function previewRows(table, limit, requestedColumns) {
185
+ const { names, indices } = resolveColumns(table, requestedColumns);
186
+ const rows = [];
187
+ for (const batch of table.batches) {
188
+ const vectors = indices.map((index) => batch.getChildAt(index));
189
+ for (let rowIndex = 0; rowIndex < batch.numRows; rowIndex += 1) {
190
+ if (rows.length >= limit) {
191
+ return rows;
192
+ }
193
+ const row = {};
194
+ for (let colIndex = 0; colIndex < names.length; colIndex += 1) {
195
+ const vector = vectors[colIndex];
196
+ row[names[colIndex]] = formatCell(vector?.get(rowIndex));
197
+ }
198
+ rows.push(row);
199
+ }
200
+ }
201
+ return rows;
202
+ }
203
+ async function loadTable(input, readOptions) {
204
+ const stdinFallback = process.stdin.isTTY ? void 0 : "-";
205
+ const source = input ?? stdinFallback;
206
+ if (!source) {
207
+ throw new Error("missing input file (pass a path or pipe stdin)");
208
+ }
209
+ if (source === "-") {
210
+ return readParquetTableFromStdin("stdin.parquet", readOptions);
211
+ }
212
+ return readParquetTableFromPath(source, readOptions);
213
+ }
214
+ async function main() {
215
+ const { input, options, limitSpecified, help, error } = parseArgs(process.argv.slice(2));
216
+ if (help) {
217
+ printUsage();
218
+ return;
219
+ }
220
+ if (error) {
221
+ process.stderr.write(`parquetlens: ${error}
222
+ `);
223
+ printUsage();
224
+ process.exitCode = 1;
225
+ return;
226
+ }
227
+ const wantsTui = resolveTuiMode(options.tuiMode, options);
228
+ if (wantsTui) {
229
+ if (!input || input === "-") {
230
+ process.stderr.write("parquetlens: tui mode requires a file path (stdin not supported)\n");
231
+ process.exitCode = 1;
232
+ return;
233
+ }
234
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
235
+ process.stderr.write("parquetlens: tui mode requires a tty, falling back to plain output\n");
236
+ } else if (!isBunRuntime()) {
237
+ const spawned = spawnBun(process.argv.slice(1));
238
+ if (spawned) {
239
+ return;
240
+ }
241
+ process.stderr.write("parquetlens: bun not found, falling back to plain output\n");
242
+ } else {
243
+ const { runTui } = await importTuiModule();
244
+ const maxRows = limitSpecified ? options.limit : void 0;
245
+ await runTui(input, { columns: options.columns, maxRows });
246
+ return;
247
+ }
248
+ }
249
+ const readOptions = {
250
+ batchSize: 1024,
251
+ columns: options.columns.length > 0 ? options.columns : void 0,
252
+ limit: options.schemaOnly ? 0 : options.limit
253
+ };
254
+ const table = await loadTable(input, readOptions);
255
+ if (options.showSchema || options.schemaOnly) {
256
+ const rowsCount = totalRows(table);
257
+ const title = input ? path.basename(input) : "stdin";
258
+ const limitSuffix = readOptions.limit ? ` (limit ${readOptions.limit})` : "";
259
+ process.stdout.write(`file: ${title}
260
+ rows loaded: ${rowsCount}${limitSuffix}
261
+ `);
262
+ process.stdout.write("schema:\n");
263
+ process.stdout.write(`${formatSchema(table)}
264
+ `);
265
+ }
266
+ if (options.schemaOnly) {
267
+ return;
268
+ }
269
+ const rows = previewRows(table, options.limit, options.columns);
270
+ if (options.json) {
271
+ for (const row of rows) {
272
+ process.stdout.write(`${JSON.stringify(row)}
273
+ `);
274
+ }
275
+ return;
276
+ }
277
+ console.table(rows);
278
+ }
279
+ function resolveTuiMode(mode, options) {
280
+ if (mode === "on") {
281
+ if (options.json || options.schemaOnly) {
282
+ return false;
283
+ }
284
+ return true;
285
+ }
286
+ if (mode === "off") {
287
+ return false;
288
+ }
289
+ return !options.json && !options.schemaOnly;
290
+ }
291
+ function isBunRuntime() {
292
+ return !!process.versions.bun;
293
+ }
294
+ function spawnBun(argv) {
295
+ const bunCheck = spawnSync("bun", ["--version"], { stdio: "ignore" });
296
+ if (bunCheck.error || bunCheck.status !== 0) {
297
+ return false;
298
+ }
299
+ const result = spawnSync("bun", [__filename, ...argv.slice(1)], {
300
+ stdio: "inherit",
301
+ env: { ...process.env, PARQUETLENS_BUN: "1" }
302
+ });
303
+ process.exitCode = result.status ?? 1;
304
+ return true;
305
+ }
306
+ async function importTuiModule() {
307
+ const extension = path.extname(__filename);
308
+ const modulePath = extension === ".js" ? "./tui.js" : "./tui.tsx";
309
+ return import(modulePath);
310
+ }
311
+ main().catch((error) => {
312
+ const message = error instanceof Error ? error.message : String(error);
313
+ process.stderr.write(`parquetlens: ${message}
314
+ `);
315
+ process.exitCode = 1;
316
+ });
317
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/main.ts"],"sourcesContent":["#!/usr/bin/env node\nimport {\n ParquetReadOptions,\n readParquetTableFromPath,\n readParquetTableFromStdin,\n} from \"@parquetlens/parquet-reader\";\nimport type { Table } from \"apache-arrow\";\nimport { spawnSync } from \"node:child_process\";\nimport path from \"node:path\";\n\ntype TuiMode = \"auto\" | \"on\" | \"off\";\n\ntype Options = {\n limit: number;\n columns: string[];\n json: boolean;\n schemaOnly: boolean;\n showSchema: boolean;\n tuiMode: TuiMode;\n};\n\ntype ParsedArgs = {\n input?: string;\n options: Options;\n limitSpecified: boolean;\n help: boolean;\n error?: string;\n};\n\nconst DEFAULT_LIMIT = 20;\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const options: Options = {\n limit: DEFAULT_LIMIT,\n columns: [],\n json: false,\n schemaOnly: false,\n showSchema: true,\n tuiMode: \"auto\",\n };\n\n let input: string | undefined;\n let limitSpecified = false;\n\n for (let i = 0; i < argv.length; i += 1) {\n const arg = argv[i];\n\n if (arg === \"--\") {\n continue;\n }\n\n if (arg === \"-h\" || arg === \"--help\") {\n return { options, limitSpecified, help: true };\n }\n\n if (arg === \"--json\") {\n options.json = true;\n options.tuiMode = \"off\";\n continue;\n }\n\n if (arg === \"--tui\") {\n options.tuiMode = \"on\";\n continue;\n }\n\n if (arg === \"--plain\" || arg === \"--no-tui\") {\n options.tuiMode = \"off\";\n continue;\n }\n\n if (arg === \"--schema\") {\n options.schemaOnly = true;\n options.tuiMode = \"off\";\n continue;\n }\n\n if (arg === \"--no-schema\") {\n options.showSchema = false;\n continue;\n }\n\n const limitValue = readOptionValue(arg, \"--limit\", argv[i + 1]);\n if (limitValue) {\n const parsed = Number.parseInt(limitValue.value, 10);\n if (!Number.isFinite(parsed) || parsed < 0) {\n return {\n options,\n limitSpecified,\n help: false,\n error: `invalid --limit value: ${limitValue.value}`,\n };\n }\n options.limit = parsed;\n limitSpecified = true;\n if (limitValue.usedNext) {\n i += 1;\n }\n continue;\n }\n\n const columnsValue = readOptionValue(arg, \"--columns\", argv[i + 1]);\n if (columnsValue) {\n const rawColumns = columnsValue.value\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean);\n options.columns = rawColumns;\n if (columnsValue.usedNext) {\n i += 1;\n }\n continue;\n }\n\n if (arg === \"-\") {\n if (input) {\n return { options, limitSpecified, help: false, error: \"unexpected extra argument: -\" };\n }\n input = arg;\n continue;\n }\n\n if (arg.startsWith(\"-\")) {\n return { options, limitSpecified, help: false, error: `unknown option: ${arg}` };\n }\n\n if (input) {\n return { options, limitSpecified, help: false, error: `unexpected extra argument: ${arg}` };\n }\n\n input = arg;\n }\n\n return { input, options, limitSpecified, help: false };\n}\n\nfunction readOptionValue(\n arg: string,\n name: \"--limit\" | \"--columns\",\n next?: string,\n): { value: string; usedNext: boolean } | null {\n if (arg === name) {\n if (!next || next.startsWith(\"-\")) {\n return null;\n }\n return { value: next, usedNext: true };\n }\n\n if (arg.startsWith(`${name}=`)) {\n return { value: arg.slice(name.length + 1), usedNext: false };\n }\n\n return null;\n}\n\nfunction printUsage(): void {\n const helpText = `parquetlens <file|-> [options]\n\noptions:\n --limit, --limit=<n> number of rows to show (default: ${DEFAULT_LIMIT})\n --columns, --columns=<c> comma-separated column list\n --schema print schema only\n --no-schema skip schema output\n --json output rows as json lines\n --tui open interactive viewer (default)\n --plain, --no-tui disable interactive viewer\n -h, --help show help\n\nexamples:\n parquetlens data.parquet --limit 25\n parquetlens data.parquet --columns=city,state\n parquetlens data.parquet --tui\n parquetlens data.parquet --plain\n parquetlens - < input.parquet\n`;\n\n process.stdout.write(helpText);\n}\n\nfunction formatSchema(table: Table): string {\n const lines = table.schema.fields.map((field, index) => {\n const typeName = String(field.type);\n return `${index + 1}. ${field.name}: ${typeName}`;\n });\n\n return lines.join(\"\\n\");\n}\n\nfunction totalRows(table: Table): number {\n return table.batches.reduce((count, batch) => count + batch.numRows, 0);\n}\n\nfunction resolveColumns(table: Table, requested: string[]): { names: string[]; indices: number[] } {\n const fields = table.schema.fields;\n const nameToIndex = new Map<string, number>();\n\n fields.forEach((field, index) => {\n nameToIndex.set(field.name, index);\n });\n\n const names = requested.length > 0 ? requested : fields.map((field) => field.name);\n const missing = names.filter((name) => !nameToIndex.has(name));\n\n if (missing.length > 0) {\n throw new Error(`unknown columns: ${missing.join(\", \")}`);\n }\n\n return {\n names,\n indices: names.map((name) => nameToIndex.get(name) ?? -1),\n };\n}\n\nfunction formatCell(value: unknown): unknown {\n if (value === null || value === undefined) {\n return null;\n }\n\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (value instanceof Uint8Array) {\n return `Uint8Array(${value.length})`;\n }\n\n if (typeof value === \"object\") {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n }\n\n return value;\n}\n\nfunction previewRows(\n table: Table,\n limit: number,\n requestedColumns: string[],\n): Record<string, unknown>[] {\n const { names, indices } = resolveColumns(table, requestedColumns);\n const rows: Record<string, unknown>[] = [];\n\n for (const batch of table.batches) {\n const vectors = indices.map((index) => batch.getChildAt(index));\n\n for (let rowIndex = 0; rowIndex < batch.numRows; rowIndex += 1) {\n if (rows.length >= limit) {\n return rows;\n }\n\n const row: Record<string, unknown> = {};\n\n for (let colIndex = 0; colIndex < names.length; colIndex += 1) {\n const vector = vectors[colIndex];\n row[names[colIndex]] = formatCell(vector?.get(rowIndex));\n }\n\n rows.push(row);\n }\n }\n\n return rows;\n}\n\nasync function loadTable(\n input: string | undefined,\n readOptions: ParquetReadOptions,\n): Promise<Table> {\n const stdinFallback = process.stdin.isTTY ? undefined : \"-\";\n const source = input ?? stdinFallback;\n\n if (!source) {\n throw new Error(\"missing input file (pass a path or pipe stdin)\");\n }\n\n if (source === \"-\") {\n return readParquetTableFromStdin(\"stdin.parquet\", readOptions);\n }\n\n return readParquetTableFromPath(source, readOptions);\n}\n\nasync function main(): Promise<void> {\n const { input, options, limitSpecified, help, error } = parseArgs(process.argv.slice(2));\n\n if (help) {\n printUsage();\n return;\n }\n\n if (error) {\n process.stderr.write(`parquetlens: ${error}\\n`);\n printUsage();\n process.exitCode = 1;\n return;\n }\n\n const wantsTui = resolveTuiMode(options.tuiMode, options);\n if (wantsTui) {\n if (!input || input === \"-\") {\n process.stderr.write(\"parquetlens: tui mode requires a file path (stdin not supported)\\n\");\n process.exitCode = 1;\n return;\n }\n\n if (!process.stdin.isTTY || !process.stdout.isTTY) {\n process.stderr.write(\"parquetlens: tui mode requires a tty, falling back to plain output\\n\");\n } else if (!isBunRuntime()) {\n const spawned = spawnBun(process.argv.slice(1));\n if (spawned) {\n return;\n }\n process.stderr.write(\"parquetlens: bun not found, falling back to plain output\\n\");\n } else {\n const { runTui } = await importTuiModule();\n const maxRows = limitSpecified ? options.limit : undefined;\n await runTui(input, { columns: options.columns, maxRows });\n return;\n }\n }\n\n const readOptions: ParquetReadOptions = {\n batchSize: 1024,\n columns: options.columns.length > 0 ? options.columns : undefined,\n limit: options.schemaOnly ? 0 : options.limit,\n };\n\n const table = await loadTable(input, readOptions);\n\n if (options.showSchema || options.schemaOnly) {\n const rowsCount = totalRows(table);\n const title = input ? path.basename(input) : \"stdin\";\n const limitSuffix = readOptions.limit ? ` (limit ${readOptions.limit})` : \"\";\n process.stdout.write(`file: ${title}\\nrows loaded: ${rowsCount}${limitSuffix}\\n`);\n process.stdout.write(\"schema:\\n\");\n process.stdout.write(`${formatSchema(table)}\\n`);\n }\n\n if (options.schemaOnly) {\n return;\n }\n\n const rows = previewRows(table, options.limit, options.columns);\n\n if (options.json) {\n for (const row of rows) {\n process.stdout.write(`${JSON.stringify(row)}\\n`);\n }\n return;\n }\n\n console.table(rows);\n}\n\nfunction resolveTuiMode(mode: TuiMode, options: Options): boolean {\n if (mode === \"on\") {\n if (options.json || options.schemaOnly) {\n return false;\n }\n return true;\n }\n\n if (mode === \"off\") {\n return false;\n }\n\n return !options.json && !options.schemaOnly;\n}\n\nfunction isBunRuntime(): boolean {\n return !!process.versions.bun;\n}\n\nfunction spawnBun(argv: string[]): boolean {\n const bunCheck = spawnSync(\"bun\", [\"--version\"], { stdio: \"ignore\" });\n if (bunCheck.error || bunCheck.status !== 0) {\n return false;\n }\n\n const result = spawnSync(\"bun\", [__filename, ...argv.slice(1)], {\n stdio: \"inherit\",\n env: { ...process.env, PARQUETLENS_BUN: \"1\" },\n });\n\n process.exitCode = result.status ?? 1;\n return true;\n}\n\nasync function importTuiModule(): Promise<typeof import(\"./tui.js\")> {\n const extension = path.extname(__filename);\n const modulePath = extension === \".js\" ? \"./tui.js\" : \"./tui.tsx\";\n return import(modulePath);\n}\n\nmain().catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`parquetlens: ${message}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}