parquetlens 0.2.3 → 0.3.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.
@@ -375,8 +375,31 @@ async function readParquetTableFromStdin(filenameHint = "stdin.parquet", options
375
375
  await temp.cleanup();
376
376
  }
377
377
  }
378
+ async function runSqlOnParquet(input, query) {
379
+ const source = await openParquetSource(input);
380
+ try {
381
+ return await source.runSql(query);
382
+ } finally {
383
+ await source.close();
384
+ }
385
+ }
386
+ async function runSqlOnParquetFromStdin(query, filenameHint = "stdin.parquet") {
387
+ const temp = await bufferStdinToTempFile(filenameHint);
388
+ try {
389
+ return await runSqlOnParquet(temp.path, query);
390
+ } finally {
391
+ await temp.cleanup();
392
+ }
393
+ }
378
394
  function createParquetSource(db, conn, fileName) {
379
395
  let metadataPromise = null;
396
+ let viewCreated = false;
397
+ const ensureDataView = () => {
398
+ if (!viewCreated) {
399
+ conn.query(`CREATE OR REPLACE VIEW data AS SELECT * FROM read_parquet(${quoteLiteral(fileName)})`);
400
+ viewCreated = true;
401
+ }
402
+ };
380
403
  return {
381
404
  readTable: async (options) => {
382
405
  const query = buildSelectQuery(fileName, options);
@@ -388,6 +411,10 @@ function createParquetSource(db, conn, fileName) {
388
411
  }
389
412
  return metadataPromise;
390
413
  },
414
+ runSql: async (query) => {
415
+ ensureDataView();
416
+ return conn.query(query);
417
+ },
391
418
  close: async () => {
392
419
  conn.close();
393
420
  db.dropFile(fileName);
@@ -480,6 +507,7 @@ export {
480
507
  openParquetSource,
481
508
  readParquetTableFromPath,
482
509
  readParquetTableFromUrl,
483
- readParquetTableFromStdin
510
+ readParquetTableFromStdin,
511
+ runSqlOnParquet,
512
+ runSqlOnParquetFromStdin
484
513
  };
485
- //# sourceMappingURL=chunk-NRRDNC7S.js.map
package/dist/main.js CHANGED
@@ -9,14 +9,17 @@ import {
9
9
  readParquetTableFromPath,
10
10
  readParquetTableFromStdin,
11
11
  readParquetTableFromUrl,
12
- resolveParquetUrl
13
- } from "./chunk-NRRDNC7S.js";
12
+ resolveParquetUrl,
13
+ runSqlOnParquet,
14
+ runSqlOnParquetFromStdin
15
+ } from "./chunk-YZLOXMC4.js";
14
16
 
15
17
  // src/main.ts
18
+ import CliTable3 from "cli-table3";
16
19
  import { spawnSync } from "child_process";
17
20
  import path from "path";
18
- import { fileURLToPath } from "url";
19
- var __filename = typeof globalThis.__filename !== "undefined" ? globalThis.__filename : fileURLToPath(import.meta.url);
21
+ import { fileURLToPath as nodeFileURLToPath } from "url";
22
+ var __parquetlens_filename = typeof __filename !== "undefined" ? __filename : nodeFileURLToPath(import.meta.url);
20
23
  var DEFAULT_LIMIT = 20;
21
24
  function parseArgs(argv) {
22
25
  const options = {
@@ -48,6 +51,7 @@ function parseArgs(argv) {
48
51
  }
49
52
  if (arg === "--plain" || arg === "--no-tui") {
50
53
  options.tuiMode = "off";
54
+ options.showSchema = false;
51
55
  continue;
52
56
  }
53
57
  if (arg === "--schema") {
@@ -86,6 +90,14 @@ function parseArgs(argv) {
86
90
  }
87
91
  continue;
88
92
  }
93
+ const sqlValue = readOptionValue(arg, "--sql", argv[i + 1]);
94
+ if (sqlValue) {
95
+ options.sql = sqlValue.value;
96
+ if (sqlValue.usedNext) {
97
+ i += 1;
98
+ }
99
+ continue;
100
+ }
89
101
  if (arg === "-") {
90
102
  if (input) {
91
103
  return { options, limitSpecified, help: false, error: "unexpected extra argument: -" };
@@ -121,6 +133,7 @@ function printUsage() {
121
133
  options:
122
134
  --limit, --limit=<n> number of rows to show (default: ${DEFAULT_LIMIT})
123
135
  --columns, --columns=<c> comma-separated column list
136
+ --sql, --sql=<query> run SQL query (use 'data' as table name)
124
137
  --schema print schema only
125
138
  --no-schema skip schema output
126
139
  --json output rows as json lines
@@ -131,6 +144,7 @@ options:
131
144
  examples:
132
145
  parquetlens data.parquet --limit 25
133
146
  parquetlens data.parquet --columns=city,state
147
+ parquetlens data.parquet --sql "SELECT city, COUNT(*) FROM data GROUP BY city"
134
148
  parquetlens hf://datasets/cfahlgren1/hub-stats/daily_papers.parquet
135
149
  parquetlens https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/main/daily_papers.parquet
136
150
  parquetlens data.parquet --plain
@@ -164,6 +178,57 @@ function resolveColumns(table, requested) {
164
178
  indices: names.map((name) => nameToIndex.get(name) ?? -1)
165
179
  };
166
180
  }
181
+ function getColumnDefs(table, requestedColumns) {
182
+ const fields = table.schema.fields;
183
+ if (requestedColumns.length === 0) {
184
+ return fields.map((f) => ({ name: f.name, type: String(f.type) }));
185
+ }
186
+ const fieldMap = new Map(fields.map((f) => [f.name, f]));
187
+ return requestedColumns.map((name) => {
188
+ const field = fieldMap.get(name);
189
+ return { name, type: field ? String(field.type) : "unknown" };
190
+ });
191
+ }
192
+ function truncateCell(value, maxWidth) {
193
+ const oneLine = value.replace(/\n/g, " ");
194
+ if (oneLine.length <= maxWidth) {
195
+ return oneLine;
196
+ }
197
+ return oneLine.slice(0, maxWidth - 3) + "...";
198
+ }
199
+ function printTable(rows, columns) {
200
+ if (rows.length === 0) {
201
+ process.stdout.write("(no rows)\n");
202
+ return;
203
+ }
204
+ const termWidth = process.stdout.columns || 120;
205
+ const numCols = columns.length;
206
+ const borderOverhead = 1 + 3 * numCols;
207
+ const availableWidth = Math.max(termWidth - borderOverhead, numCols * 6);
208
+ const idealWidths = columns.map((c) => {
209
+ const headerLen = `${c.name}: ${c.type}`.length;
210
+ const maxContent = rows.reduce((max, row) => {
211
+ const val = String(row[c.name] ?? "").replace(/\n/g, " ");
212
+ return Math.max(max, val.length);
213
+ }, 0);
214
+ return Math.min(60, Math.max(headerLen, maxContent));
215
+ });
216
+ const totalIdeal = idealWidths.reduce((sum, w) => sum + w, 0);
217
+ const scale = Math.min(1, availableWidth / totalIdeal);
218
+ const colWidths = idealWidths.map((ideal) => Math.max(6, Math.floor(ideal * scale)));
219
+ const headers = columns.map((c, i) => truncateCell(`${c.name}: ${c.type}`, colWidths[i]));
220
+ const table = new CliTable3({
221
+ head: headers,
222
+ style: { head: [], border: [] },
223
+ colWidths: colWidths.map((w) => w + 2),
224
+ // +2 for padding
225
+ wordWrap: false
226
+ });
227
+ for (const row of rows) {
228
+ table.push(columns.map((c, i) => truncateCell(String(row[c.name] ?? ""), colWidths[i])));
229
+ }
230
+ process.stdout.write(table.toString() + "\n");
231
+ }
167
232
  function formatCell(value) {
168
233
  if (value === null || value === void 0) {
169
234
  return null;
@@ -232,6 +297,41 @@ async function main() {
232
297
  process.exitCode = 1;
233
298
  return;
234
299
  }
300
+ if (options.sql) {
301
+ const stdinFallback = process.stdin.isTTY ? void 0 : "-";
302
+ const source = input ?? stdinFallback;
303
+ if (!source) {
304
+ process.stderr.write("parquetlens: missing input file for SQL query\n");
305
+ process.exitCode = 1;
306
+ return;
307
+ }
308
+ const table2 = source === "-" ? await runSqlOnParquetFromStdin(options.sql) : await runSqlOnParquet(source, options.sql);
309
+ const wantsSqlTui = !options.json && options.tuiMode !== "off" && source !== "-" && process.stdin.isTTY && process.stdout.isTTY;
310
+ if (wantsSqlTui) {
311
+ if (isBunRuntime()) {
312
+ const { runTuiWithTable } = await importTuiModule();
313
+ const title = `SQL: ${options.sql.slice(0, 50)}${options.sql.length > 50 ? "..." : ""}`;
314
+ await runTuiWithTable(table2, title, { columns: [], maxRows: options.limit });
315
+ return;
316
+ }
317
+ const spawned = spawnBun(process.argv.slice(1));
318
+ if (spawned) {
319
+ return;
320
+ }
321
+ process.stderr.write("parquetlens: bun not found, falling back to plain output\n");
322
+ }
323
+ const sqlColumns = getColumnDefs(table2, []);
324
+ const rows2 = previewRows(table2, options.limit, []);
325
+ if (options.json) {
326
+ for (const row of rows2) {
327
+ process.stdout.write(`${JSON.stringify(row)}
328
+ `);
329
+ }
330
+ } else {
331
+ printTable(rows2, sqlColumns);
332
+ }
333
+ return;
334
+ }
235
335
  const wantsTui = resolveTuiMode(options.tuiMode, options);
236
336
  if (wantsTui) {
237
337
  if (!input || input === "-") {
@@ -276,6 +376,7 @@ rows loaded: ${rowsCount}${limitSuffix}
276
376
  if (options.schemaOnly) {
277
377
  return;
278
378
  }
379
+ const columnDefs = getColumnDefs(table, options.columns);
279
380
  const rows = previewRows(table, options.limit, options.columns);
280
381
  if (options.json) {
281
382
  for (const row of rows) {
@@ -284,7 +385,7 @@ rows loaded: ${rowsCount}${limitSuffix}
284
385
  }
285
386
  return;
286
387
  }
287
- console.table(rows);
388
+ printTable(rows, columnDefs);
288
389
  }
289
390
  function resolveTuiMode(mode, options) {
290
391
  if (mode === "on") {
@@ -306,7 +407,7 @@ function spawnBun(argv) {
306
407
  if (bunCheck.error || bunCheck.status !== 0) {
307
408
  return false;
308
409
  }
309
- const result = spawnSync("bun", [__filename, ...argv.slice(1)], {
410
+ const result = spawnSync("bun", [__parquetlens_filename, ...argv.slice(1)], {
310
411
  stdio: "inherit",
311
412
  env: { ...process.env, PARQUETLENS_BUN: "1" }
312
413
  });
@@ -314,7 +415,7 @@ function spawnBun(argv) {
314
415
  return true;
315
416
  }
316
417
  async function importTuiModule() {
317
- const extension = path.extname(__filename);
418
+ const extension = path.extname(__parquetlens_filename);
318
419
  const modulePath = extension === ".js" ? "./tui.js" : "./tui.tsx";
319
420
  return import(modulePath);
320
421
  }
@@ -324,4 +425,3 @@ main().catch((error) => {
324
425
  `);
325
426
  process.exitCode = 1;
326
427
  });
327
- //# sourceMappingURL=main.js.map
package/dist/tui.js CHANGED
@@ -6,7 +6,7 @@ const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = dirname(__filename);
7
7
  import {
8
8
  openParquetSource
9
- } from "./chunk-NRRDNC7S.js";
9
+ } from "./chunk-YZLOXMC4.js";
10
10
 
11
11
  // src/tui.tsx
12
12
  import { createCliRenderer } from "@opentui/core";
@@ -38,8 +38,7 @@ var THEME = {
38
38
  muted: "#6272a4",
39
39
  stripe: "#252733"
40
40
  };
41
- async function runTui(input, options) {
42
- const source = await openParquetSource(input);
41
+ async function createTuiRenderer() {
43
42
  const renderer = await createCliRenderer({
44
43
  exitOnCtrlC: true,
45
44
  useAlternateScreen: true,
@@ -53,92 +52,66 @@ async function runTui(input, options) {
53
52
  root.unmount();
54
53
  renderer.destroy();
55
54
  };
56
- root.render(/* @__PURE__ */ jsx(App, { source, filePath: input, options, onExit: handleExit }));
55
+ return { root, handleExit };
57
56
  }
58
- function App({ source, filePath, options, onExit }) {
59
- const { width, height } = useTerminalDimensions();
60
- const pageSize = Math.max(1, height - RESERVED_LINES);
61
- const [offset, setOffset] = useState(0);
57
+ async function runTui(input, options) {
58
+ const source = await openParquetSource(input);
59
+ const initialLimit = options.maxRows ? Math.min(50, options.maxRows) : 50;
60
+ const table = await source.readTable({
61
+ batchSize: options.batchSize ?? 1024,
62
+ columns: options.columns.length > 0 ? options.columns : void 0,
63
+ limit: initialLimit,
64
+ offset: 0
65
+ });
66
+ const initialColumns = table.schema.fields.map((field) => ({
67
+ name: field.name,
68
+ type: formatArrowType(field.type)
69
+ }));
70
+ const initialRows = tableToGridRows(table, initialColumns.map((c) => c.name));
71
+ const initialGrid = { columns: initialColumns, rows: initialRows };
72
+ const metadata = await source.readMetadata().catch(() => null);
73
+ const { root, handleExit } = await createTuiRenderer();
74
+ root.render(
75
+ /* @__PURE__ */ jsx(
76
+ App,
77
+ {
78
+ source,
79
+ filePath: input,
80
+ options,
81
+ onExit: handleExit,
82
+ initialGrid,
83
+ initialMetadata: metadata,
84
+ initialKnownTotal: initialRows.length < initialLimit ? initialRows.length : null
85
+ }
86
+ )
87
+ );
88
+ }
89
+ async function runTuiWithTable(table, title, options) {
90
+ const { root, handleExit } = await createTuiRenderer();
91
+ root.render(/* @__PURE__ */ jsx(StaticApp, { table, title, options, onExit: handleExit }));
92
+ }
93
+ function TableViewer({
94
+ grid,
95
+ title,
96
+ offset,
97
+ setOffset,
98
+ maxOffset,
99
+ totalRows,
100
+ pageSize,
101
+ loading = false,
102
+ error = null,
103
+ notice = null,
104
+ metadata = null,
105
+ onExit,
106
+ onCopyError
107
+ }) {
108
+ const { width } = useTerminalDimensions();
62
109
  const [xOffset, setXOffset] = useState(0);
63
- const [grid, setGrid] = useState({ columns: [], rows: [] });
64
110
  const [selection, setSelection] = useState(null);
65
111
  const [sidebarOpen, setSidebarOpen] = useState(false);
66
- const [loading, setLoading] = useState(true);
67
- const [error, setError] = useState(null);
68
- const [notice, setNotice] = useState(null);
69
- const [metadata, setMetadata] = useState(null);
70
- const [knownTotalRows, setKnownTotalRows] = useState(null);
71
- const noticeTimer = useRef(null);
72
- const effectiveTotal = options.maxRows ?? knownTotalRows;
73
- const maxOffset = effectiveTotal === void 0 || effectiveTotal === null ? void 0 : Math.max(0, effectiveTotal - pageSize);
74
112
  const sidebarWidth = sidebarOpen ? Math.min(width, Math.max(SIDEBAR_MIN_WIDTH, Math.floor(width * SIDEBAR_WIDTH_RATIO))) : 0;
75
113
  const tableWidth = Math.max(0, width - (sidebarOpen ? sidebarWidth + PANEL_GAP : 0));
76
114
  const tableContentWidth = Math.max(0, tableWidth - CONTENT_BORDER_WIDTH);
77
- const columnsToRead = options.columns;
78
- useEffect(() => {
79
- return () => {
80
- void source.close();
81
- };
82
- }, [source]);
83
- useEffect(() => {
84
- let canceled = false;
85
- const loadWindow = async () => {
86
- setLoading(true);
87
- setError(null);
88
- try {
89
- const limit = options.maxRows ? Math.max(0, Math.min(pageSize, options.maxRows - offset)) : pageSize;
90
- const readOptions = {
91
- batchSize: options.batchSize ?? 1024,
92
- columns: columnsToRead.length > 0 ? columnsToRead : void 0,
93
- limit,
94
- offset
95
- };
96
- const table = await source.readTable(readOptions);
97
- const columns = table.schema.fields.map((field) => ({
98
- name: field.name,
99
- type: formatArrowType(field.type)
100
- }));
101
- const rows = tableToRows(
102
- table,
103
- columns.map((c) => c.name)
104
- );
105
- if (!canceled) {
106
- setGrid({ columns, rows });
107
- if (rows.length < limit) {
108
- setKnownTotalRows(offset + rows.length);
109
- }
110
- }
111
- } catch (caught) {
112
- const message = caught instanceof Error ? caught.message : String(caught);
113
- if (!canceled) {
114
- setError(message);
115
- }
116
- } finally {
117
- if (!canceled) {
118
- setLoading(false);
119
- }
120
- }
121
- };
122
- loadWindow();
123
- return () => {
124
- canceled = true;
125
- };
126
- }, [columnsToRead, offset, options.batchSize, options.maxRows, pageSize, source]);
127
- useEffect(() => {
128
- let canceled = false;
129
- source.readMetadata().then((meta) => {
130
- if (!canceled) {
131
- setMetadata(meta);
132
- }
133
- }).catch(() => {
134
- if (!canceled) {
135
- setMetadata(null);
136
- }
137
- });
138
- return () => {
139
- canceled = true;
140
- };
141
- }, [source]);
142
115
  const gridLines = useMemo(
143
116
  () => buildGridLines(grid, offset, tableContentWidth),
144
117
  [grid, offset, tableContentWidth]
@@ -163,22 +136,6 @@ function App({ source, filePath, options, onExit }) {
163
136
  setSidebarOpen(true);
164
137
  }
165
138
  }, [error]);
166
- useEffect(() => {
167
- return () => {
168
- if (noticeTimer.current) {
169
- clearTimeout(noticeTimer.current);
170
- }
171
- };
172
- }, []);
173
- const showNotice = (message) => {
174
- setNotice(message);
175
- if (noticeTimer.current) {
176
- clearTimeout(noticeTimer.current);
177
- }
178
- noticeTimer.current = setTimeout(() => {
179
- setNotice(null);
180
- }, 2e3);
181
- };
182
139
  useKeyboard((key) => {
183
140
  if (key.ctrl && key.name === "c" || key.name === "escape" || key.name === "q") {
184
141
  if (sidebarOpen && key.name === "escape") {
@@ -216,7 +173,7 @@ function App({ source, filePath, options, onExit }) {
216
173
  setOffset(maxOffset);
217
174
  return;
218
175
  }
219
- if (key.name === "return" || key.name === "enter") {
176
+ if (key.name === "return" || key.name === "enter" || key.name === "s") {
220
177
  setSidebarOpen((current) => !current);
221
178
  return;
222
179
  }
@@ -224,17 +181,12 @@ function App({ source, filePath, options, onExit }) {
224
181
  setSidebarOpen(false);
225
182
  return;
226
183
  }
227
- if (key.name === "s") {
228
- setSidebarOpen((current) => !current);
229
- return;
230
- }
231
184
  if (key.name === "e" && error) {
232
185
  setSidebarOpen(true);
233
186
  return;
234
187
  }
235
- if (key.name === "y" && error) {
236
- const copied = copyToClipboard(error);
237
- showNotice(copied ? "copied error to clipboard" : "clipboard unavailable");
188
+ if (key.name === "y" && error && onCopyError) {
189
+ onCopyError();
238
190
  return;
239
191
  }
240
192
  if (key.name === "left" || key.name === "h") {
@@ -255,13 +207,13 @@ function App({ source, filePath, options, onExit }) {
255
207
  const metaFlags = getMetadataFlags(metadata);
256
208
  return /* @__PURE__ */ jsxs("box", { flexDirection: "column", width: "100%", height: "100%", backgroundColor: THEME.background, children: [
257
209
  /* @__PURE__ */ jsx("box", { backgroundColor: THEME.header, border: true, borderColor: THEME.border, children: renderHeader({
258
- filePath,
210
+ filePath: title,
259
211
  offset,
260
212
  rows: grid.rows.length,
261
213
  columns: grid.columns.length,
262
214
  loading,
263
215
  error,
264
- maxRows: effectiveTotal ?? void 0,
216
+ maxRows: totalRows,
265
217
  optimized: metaFlags.optimized,
266
218
  createdBy: metaFlags.createdBy
267
219
  }) }),
@@ -275,9 +227,7 @@ function App({ source, filePath, options, onExit }) {
275
227
  flexGrow: 1,
276
228
  width: sidebarOpen ? tableWidth : "100%",
277
229
  onMouseScroll: (event) => {
278
- if (!event.scroll) {
279
- return;
280
- }
230
+ if (!event.scroll) return;
281
231
  const delta = Math.max(1, event.scroll.delta);
282
232
  const step = delta * SCROLL_STEP;
283
233
  if (event.scroll.direction === "up") {
@@ -308,9 +258,7 @@ function App({ source, filePath, options, onExit }) {
308
258
  bg: isSelected ? THEME.header : index % 2 === 0 ? THEME.background : THEME.stripe,
309
259
  onMouseDown: (event) => {
310
260
  const target = event.target;
311
- if (!target) {
312
- return;
313
- }
261
+ if (!target) return;
314
262
  const localX = Math.max(0, event.x - target.x);
315
263
  const absoluteX = localX + xOffset;
316
264
  const colIndex = findColumnIndex(absoluteX, gridLines.columnRanges);
@@ -356,9 +304,7 @@ function App({ source, filePath, options, onExit }) {
356
304
  wrapMode: "none",
357
305
  truncate: true,
358
306
  fg: THEME.accent,
359
- onMouseDown: () => {
360
- setSidebarOpen(false);
361
- },
307
+ onMouseDown: () => setSidebarOpen(false),
362
308
  children: "[ close ]"
363
309
  }
364
310
  )
@@ -366,9 +312,162 @@ function App({ source, filePath, options, onExit }) {
366
312
  }
367
313
  ) : null
368
314
  ] }),
369
- /* @__PURE__ */ jsx("box", { backgroundColor: THEME.header, border: true, borderColor: THEME.border, children: renderFooter(Boolean(error), notice) })
315
+ /* @__PURE__ */ jsx("box", { backgroundColor: THEME.header, border: true, borderColor: THEME.border, children: renderFooter(!!error, notice) })
370
316
  ] });
371
317
  }
318
+ function App({ source, filePath, options, onExit, initialGrid, initialMetadata, initialKnownTotal }) {
319
+ const { height } = useTerminalDimensions();
320
+ const pageSize = Math.max(1, height - RESERVED_LINES);
321
+ const [offset, setOffset] = useState(0);
322
+ const [grid, setGrid] = useState(initialGrid ?? { columns: [], rows: [] });
323
+ const [loading, setLoading] = useState(!initialGrid);
324
+ const [error, setError] = useState(null);
325
+ const [notice, setNotice] = useState(null);
326
+ const [metadata, setMetadata] = useState(initialMetadata ?? null);
327
+ const [knownTotalRows, setKnownTotalRows] = useState(initialKnownTotal ?? null);
328
+ const noticeTimer = useRef(null);
329
+ const initialLoadDone = useRef(!!initialGrid);
330
+ const effectiveTotal = options.maxRows ?? knownTotalRows;
331
+ const maxOffset = effectiveTotal === void 0 || effectiveTotal === null ? void 0 : Math.max(0, effectiveTotal - pageSize);
332
+ const columnsToRead = options.columns;
333
+ useEffect(() => {
334
+ return () => {
335
+ void source.close();
336
+ };
337
+ }, [source]);
338
+ useEffect(() => {
339
+ if (offset === 0 && initialLoadDone.current) {
340
+ initialLoadDone.current = false;
341
+ return;
342
+ }
343
+ let canceled = false;
344
+ const loadWindow = async () => {
345
+ setLoading(true);
346
+ setError(null);
347
+ try {
348
+ const limit = options.maxRows ? Math.max(0, Math.min(pageSize, options.maxRows - offset)) : pageSize;
349
+ const readOptions = {
350
+ batchSize: options.batchSize ?? 1024,
351
+ columns: columnsToRead.length > 0 ? columnsToRead : void 0,
352
+ limit,
353
+ offset
354
+ };
355
+ const table = await source.readTable(readOptions);
356
+ const columns = table.schema.fields.map((field) => ({
357
+ name: field.name,
358
+ type: formatArrowType(field.type)
359
+ }));
360
+ const rows = tableToGridRows(
361
+ table,
362
+ columns.map((c) => c.name)
363
+ );
364
+ if (!canceled) {
365
+ setGrid({ columns, rows });
366
+ if (rows.length < limit) {
367
+ setKnownTotalRows(offset + rows.length);
368
+ }
369
+ }
370
+ } catch (caught) {
371
+ const message = caught instanceof Error ? caught.message : String(caught);
372
+ if (!canceled) {
373
+ setError(message);
374
+ }
375
+ } finally {
376
+ if (!canceled) {
377
+ setLoading(false);
378
+ }
379
+ }
380
+ };
381
+ loadWindow();
382
+ return () => {
383
+ canceled = true;
384
+ };
385
+ }, [columnsToRead, offset, options.batchSize, options.maxRows, pageSize, source]);
386
+ useEffect(() => {
387
+ if (initialMetadata !== void 0) return;
388
+ let canceled = false;
389
+ source.readMetadata().then((meta) => {
390
+ if (!canceled) setMetadata(meta);
391
+ }).catch(() => {
392
+ if (!canceled) setMetadata(null);
393
+ });
394
+ return () => {
395
+ canceled = true;
396
+ };
397
+ }, [initialMetadata, source]);
398
+ useEffect(() => {
399
+ return () => {
400
+ if (noticeTimer.current) clearTimeout(noticeTimer.current);
401
+ };
402
+ }, []);
403
+ const handleCopyError = () => {
404
+ if (!error) return;
405
+ const copied = copyToClipboard(error);
406
+ setNotice(copied ? "copied error to clipboard" : "clipboard unavailable");
407
+ if (noticeTimer.current) clearTimeout(noticeTimer.current);
408
+ noticeTimer.current = setTimeout(() => setNotice(null), 2e3);
409
+ };
410
+ return /* @__PURE__ */ jsx(
411
+ TableViewer,
412
+ {
413
+ grid,
414
+ title: filePath,
415
+ offset,
416
+ setOffset,
417
+ maxOffset,
418
+ totalRows: effectiveTotal ?? void 0,
419
+ pageSize,
420
+ loading,
421
+ error,
422
+ notice,
423
+ metadata,
424
+ onExit,
425
+ onCopyError: handleCopyError
426
+ }
427
+ );
428
+ }
429
+ function StaticApp({ table, title, options, onExit }) {
430
+ const { height } = useTerminalDimensions();
431
+ const pageSize = Math.max(1, height - RESERVED_LINES);
432
+ const [offset, setOffset] = useState(0);
433
+ const columns = useMemo(
434
+ () => table.schema.fields.map((field) => ({
435
+ name: field.name,
436
+ type: formatArrowType(field.type)
437
+ })),
438
+ [table]
439
+ );
440
+ const allRows = useMemo(
441
+ () => tableToGridRows(
442
+ table,
443
+ columns.map((c) => c.name)
444
+ ),
445
+ [table, columns]
446
+ );
447
+ const totalRows = allRows.length;
448
+ const maxOffset = Math.max(0, totalRows - pageSize);
449
+ const visibleRows = useMemo(
450
+ () => allRows.slice(offset, offset + pageSize),
451
+ [allRows, offset, pageSize]
452
+ );
453
+ const grid = useMemo(
454
+ () => ({ columns, rows: visibleRows }),
455
+ [columns, visibleRows]
456
+ );
457
+ return /* @__PURE__ */ jsx(
458
+ TableViewer,
459
+ {
460
+ grid,
461
+ title,
462
+ offset,
463
+ setOffset,
464
+ maxOffset,
465
+ totalRows,
466
+ pageSize,
467
+ onExit
468
+ }
469
+ );
470
+ }
372
471
  function renderHeader(props) {
373
472
  const { filePath, offset, rows, columns, loading, error, maxRows, createdBy, optimized } = props;
374
473
  const start = rows > 0 ? offset + 1 : offset;
@@ -587,7 +686,7 @@ function getMetadataFlags(metadata) {
587
686
  createdBy: metadata.createdBy
588
687
  };
589
688
  }
590
- function tableToRows(table, columns) {
689
+ function tableToGridRows(table, columns) {
591
690
  const rows = [];
592
691
  for (const batch of table.batches) {
593
692
  const vectors = columns.map((_, index) => batch.getChildAt(index));
@@ -642,6 +741,6 @@ function copyToClipboard(value) {
642
741
  return false;
643
742
  }
644
743
  export {
645
- runTui
744
+ runTui,
745
+ runTuiWithTable
646
746
  };
647
- //# sourceMappingURL=tui.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "parquetlens",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "A fast, interactive TUI for viewing Parquet files",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,6 +35,7 @@
35
35
  "@opentui/core": "^0.1.74",
36
36
  "@opentui/react": "^0.1.74",
37
37
  "apache-arrow": "^17.0.0",
38
+ "cli-table3": "^0.6.5",
38
39
  "react": "^19.2.3"
39
40
  },
40
41
  "devDependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../packages/parquet-reader/src/index.ts","../../../packages/parquet-reader/src/urls.ts"],"sourcesContent":["import { createWriteStream, readFileSync, rmSync } from \"node:fs\";\nimport { promises as fs } from \"node:fs\";\nimport { randomUUID } from \"node:crypto\";\nimport { spawnSync } from \"node:child_process\";\nimport { Buffer } from \"node:buffer\";\nimport { createRequire as nodeCreateRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\n\nimport type { Table } from \"apache-arrow\";\nimport {\n DuckDBAccessMode,\n DuckDBBundles,\n DuckDBConnection,\n DuckDBDataProtocol,\n DuckDBBindings,\n FileFlags,\n NODE_RUNTIME,\n VoidLogger,\n createDuckDB,\n failWith,\n readString,\n} from \"@duckdb/duckdb-wasm/blocking\";\nimport type { DuckDBModule } from \"@duckdb/duckdb-wasm/blocking\";\n\nimport { resolveParquetUrl } from \"./urls.js\";\n\nexport type TempParquetFile = {\n path: string;\n cleanup: () => Promise<void>;\n};\n\nexport type ParquetReadOptions = {\n batchSize?: number;\n columns?: string[];\n limit?: number;\n offset?: number;\n rowGroups?: number[];\n};\n\nexport type ParquetFileMetadata = {\n createdBy?: string;\n keyValueMetadata: Record<string, string>;\n};\n\nexport type ParquetSource = {\n readTable: (options?: ParquetReadOptions) => Promise<Table>;\n readMetadata: () => Promise<ParquetFileMetadata>;\n close: () => Promise<void>;\n};\n\nlet duckDbPromise: Promise<DuckDBBindings> | null = null;\nlet httpRuntimePatched = false;\n\ntype HttpBuffer = {\n dataPtr: number;\n size: number;\n};\n\nconst httpBuffers = new Map<number, HttpBuffer>();\n\ntype WasmModule = DuckDBModule & {\n HEAPU8: Uint8Array;\n HEAPF64: Float64Array;\n _malloc: (size: number) => number;\n _free: (ptr: number) => void;\n};\n\nasync function getDuckDb(): Promise<DuckDBBindings> {\n if (!duckDbPromise) {\n duckDbPromise = (async () => {\n ensureHttpRuntimeSupport();\n const bundles = getDuckDbBundles();\n const db = await createDuckDB(bundles, new VoidLogger(), NODE_RUNTIME);\n await db.instantiate();\n db.open({\n accessMode: DuckDBAccessMode.READ_WRITE,\n filesystem: {\n allowFullHTTPReads: true,\n },\n });\n return db;\n })();\n }\n\n return duckDbPromise;\n}\n\nfunction getDuckDbBundles(): DuckDBBundles {\n const localRequire = nodeCreateRequire(import.meta.url);\n const mvpModule = localRequire.resolve(\"@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm\");\n const mvpWorker = localRequire.resolve(\"@duckdb/duckdb-wasm/dist/duckdb-node-mvp.worker.cjs\");\n const ehModule = localRequire.resolve(\"@duckdb/duckdb-wasm/dist/duckdb-eh.wasm\");\n const ehWorker = localRequire.resolve(\"@duckdb/duckdb-wasm/dist/duckdb-node-eh.worker.cjs\");\n\n return {\n mvp: {\n mainModule: mvpModule,\n mainWorker: mvpWorker,\n },\n eh: {\n mainModule: ehModule,\n mainWorker: ehWorker,\n },\n };\n}\n\nfunction ensureHttpRuntimeSupport(): void {\n if (httpRuntimePatched) {\n return;\n }\n httpRuntimePatched = true;\n\n const nodeOpenFile = NODE_RUNTIME.openFile as unknown as (\n mod: WasmModule,\n fileId: number,\n flags: FileFlags,\n ) => number;\n const nodeReadFile = NODE_RUNTIME.readFile as unknown as (\n mod: WasmModule,\n fileId: number,\n buffer: number,\n bytes: number,\n location: number,\n ) => number;\n const nodeCheckFile = NODE_RUNTIME.checkFile.bind(NODE_RUNTIME);\n const nodeGlob = NODE_RUNTIME.glob.bind(NODE_RUNTIME);\n const nodeCloseFile = NODE_RUNTIME.closeFile.bind(NODE_RUNTIME);\n const nodeGetLastModified = NODE_RUNTIME.getLastFileModificationTime.bind(NODE_RUNTIME);\n\n NODE_RUNTIME.openFile = (mod: WasmModule, fileId: number, flags: FileFlags): number => {\n const file = NODE_RUNTIME.resolveFileInfo(mod, fileId);\n if (!file || file.dataProtocol !== DuckDBDataProtocol.HTTP) {\n return nodeOpenFile(mod, fileId, flags);\n }\n\n if (flags & FileFlags.FILE_FLAGS_WRITE || flags & FileFlags.FILE_FLAGS_APPEND) {\n failWith(mod, `Opening file ${file.fileName} failed: HTTP writes are not supported`);\n return 0;\n }\n\n if (!(flags & FileFlags.FILE_FLAGS_READ)) {\n failWith(mod, `Opening file ${file.fileName} failed: unsupported file flags: ${flags}`);\n return 0;\n }\n\n if (!file.dataUrl) {\n failWith(mod, `Opening file ${file.fileName} failed: missing data URL`);\n return 0;\n }\n\n const allowFull = file.allowFullHttpReads ?? true;\n const forceFull = file.forceFullHttpReads ?? false;\n\n if (!forceFull) {\n try {\n const probe = requestHttpRange(file.dataUrl, 0, 0);\n if (probe.status === 206) {\n const total =\n parseContentRangeTotal(probe.headers[\"content-range\"]) ??\n parseContentLength(probe.headers[\"content-length\"]);\n if (total !== null) {\n return buildOpenResult(mod, total, 0);\n }\n }\n\n if (probe.status === 200 && allowFull) {\n const dataPtr = writeResponseToHeap(mod, probe.bytes);\n httpBuffers.set(fileId, { dataPtr, size: probe.bytes.length });\n return buildOpenResult(mod, probe.bytes.length, dataPtr);\n }\n } catch (error) {\n if (!allowFull) {\n failWith(mod, `Opening file ${file.fileName} failed: ${String(error)}`);\n return 0;\n }\n }\n }\n\n if (allowFull) {\n try {\n const full = requestHttp(file.dataUrl);\n if (full.status === 200) {\n const dataPtr = writeResponseToHeap(mod, full.bytes);\n httpBuffers.set(fileId, { dataPtr, size: full.bytes.length });\n return buildOpenResult(mod, full.bytes.length, dataPtr);\n }\n } catch (error) {\n failWith(mod, `Opening file ${file.fileName} failed: ${String(error)}`);\n return 0;\n }\n }\n\n failWith(mod, `Opening file ${file.fileName} failed: HTTP range requests unavailable`);\n return 0;\n };\n\n NODE_RUNTIME.readFile = (\n mod: WasmModule,\n fileId: number,\n buffer: number,\n bytes: number,\n location: number,\n ): number => {\n if (bytes === 0) {\n return 0;\n }\n\n const file = NODE_RUNTIME.resolveFileInfo(mod, fileId);\n if (!file || file.dataProtocol !== DuckDBDataProtocol.HTTP) {\n return nodeReadFile(mod, fileId, buffer, bytes, location);\n }\n\n const cached = httpBuffers.get(fileId);\n if (cached) {\n const sliceStart = Math.max(0, location);\n const sliceEnd = Math.min(cached.size, location + bytes);\n const length = Math.max(0, sliceEnd - sliceStart);\n if (length > 0) {\n const src = mod.HEAPU8.subarray(cached.dataPtr + sliceStart, cached.dataPtr + sliceEnd);\n mod.HEAPU8.set(src, buffer);\n }\n return length;\n }\n\n if (!file.dataUrl) {\n failWith(mod, `Reading file ${file.fileName} failed: missing data URL`);\n return 0;\n }\n\n try {\n const response = requestHttpRange(file.dataUrl, location, location + bytes - 1);\n if (response.status === 206 || (response.status === 200 && location === 0)) {\n const length = Math.min(bytes, response.bytes.length);\n if (length > 0) {\n mod.HEAPU8.set(response.bytes.subarray(0, length), buffer);\n }\n return length;\n }\n\n failWith(mod, `Reading file ${file.fileName} failed with HTTP ${response.status}`);\n return 0;\n } catch (error) {\n failWith(mod, `Reading file ${file.fileName} failed: ${String(error)}`);\n return 0;\n }\n };\n\n NODE_RUNTIME.checkFile = (mod: DuckDBModule, pathPtr: number, pathLen: number): boolean => {\n const path = readString(mod, pathPtr, pathLen);\n if (isHttpUrl(path)) {\n const response = requestHttpHead(path);\n return response.status === 200 || response.status === 206;\n }\n return nodeCheckFile(mod, pathPtr, pathLen);\n };\n\n NODE_RUNTIME.glob = (mod: DuckDBModule, pathPtr: number, pathLen: number): void => {\n const path = readString(mod, pathPtr, pathLen);\n if (isHttpUrl(path)) {\n const response = requestHttpHead(path);\n if (response.status === 200 || response.status === 206) {\n mod.ccall(\"duckdb_web_fs_glob_add_path\", null, [\"string\"], [path]);\n }\n return;\n }\n\n return nodeGlob(mod, pathPtr, pathLen);\n };\n\n NODE_RUNTIME.closeFile = (mod: DuckDBModule, fileId: number): void => {\n const cached = httpBuffers.get(fileId);\n if (cached) {\n if (cached.dataPtr) {\n (mod as WasmModule)._free(cached.dataPtr);\n }\n httpBuffers.delete(fileId);\n }\n nodeCloseFile(mod, fileId);\n };\n\n NODE_RUNTIME.getLastFileModificationTime = (mod: DuckDBModule, fileId: number): number => {\n const file = NODE_RUNTIME.resolveFileInfo(mod, fileId);\n if (file?.dataProtocol === DuckDBDataProtocol.HTTP) {\n return Date.now() / 1000;\n }\n return nodeGetLastModified(mod, fileId);\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n\nfunction buildOpenResult(mod: WasmModule, size: number, dataPtr: number): number {\n const result = mod._malloc(2 * 8);\n mod.HEAPF64[(result >> 3) + 0] = +size;\n mod.HEAPF64[(result >> 3) + 1] = dataPtr;\n return result;\n}\n\nfunction writeResponseToHeap(mod: WasmModule, bytes: Uint8Array): number {\n const dataPtr = mod._malloc(bytes.byteLength);\n mod.HEAPU8.set(bytes, dataPtr);\n return dataPtr;\n}\n\nfunction parseContentRangeTotal(contentRange: string | null): number | null {\n if (!contentRange) {\n return null;\n }\n const [, total] = contentRange.split(\"/\");\n if (!total) {\n return null;\n }\n const parsed = Number.parseInt(total, 10);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\nfunction parseContentLength(contentLength: string | null): number | null {\n if (!contentLength) {\n return null;\n }\n const parsed = Number.parseInt(contentLength, 10);\n return Number.isFinite(parsed) ? parsed : null;\n}\n\ntype HttpResponse = {\n status: number;\n bytes: Uint8Array;\n headers: Record<string, string>;\n};\n\nfunction requestHttp(url: string): HttpResponse {\n return requestCurl([url]);\n}\n\nfunction requestHttpHead(url: string): HttpResponse {\n return requestCurl([\"-I\", url]);\n}\n\nfunction requestHttpRange(url: string, start: number, end: number): HttpResponse {\n return requestCurl([\"-r\", `${start}-${end}`, url]);\n}\n\nfunction requestCurl(args: string[]): HttpResponse {\n const tempPath = path.join(tmpdir(), `parquetlens-http-${randomUUID()}`);\n try {\n const result = spawnSync(\"curl\", [\"-sS\", \"-L\", \"-D\", \"-\", \"-o\", tempPath, ...args], {\n encoding: \"buffer\",\n maxBuffer: 4 * 1024 * 1024,\n });\n\n if (result.error) {\n if ((result.error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new Error(\"curl not found (required for HTTP range reads)\");\n }\n throw result.error;\n }\n if (result.status !== 0) {\n const stderr = result.stderr?.toString(\"utf8\").trim();\n throw new Error(stderr || \"curl failed\");\n }\n\n const body = readFileSync(tempPath);\n return parseCurlResponse(Buffer.from(result.stdout ?? []), body);\n } finally {\n rmSync(tempPath, { force: true });\n }\n}\n\nfunction parseCurlResponse(headersBuffer: Buffer, body: Buffer): HttpResponse {\n const headerBlob = headersBuffer.toString(\"latin1\");\n const blocks = headerBlob.split(/\\r\\n\\r\\n/).filter(Boolean);\n const lastBlock = blocks[blocks.length - 1] ?? \"\";\n const lines = lastBlock.split(/\\r\\n/).filter(Boolean);\n const statusLine = lines.shift() ?? \"\";\n const statusToken = statusLine.split(\" \")[1] ?? \"\";\n const status = Number.parseInt(statusToken, 10);\n const headers: Record<string, string> = {};\n\n for (const line of lines) {\n const index = line.indexOf(\":\");\n if (index === -1) {\n continue;\n }\n const key = line.slice(0, index).trim().toLowerCase();\n const value = line.slice(index + 1).trim();\n headers[key] = value;\n }\n\n return {\n status: Number.isFinite(status) ? status : 0,\n bytes: new Uint8Array(body),\n headers,\n };\n}\n\nexport async function openParquetSourceFromPath(filePath: string): Promise<ParquetSource> {\n const db = await getDuckDb();\n const conn = db.connect();\n const fileName = buildDuckDbFileName(filePath);\n\n db.registerFileURL(fileName, filePath, DuckDBDataProtocol.NODE_FS, true);\n\n return createParquetSource(db, conn, fileName);\n}\n\nexport async function openParquetSourceFromUrl(input: string): Promise<ParquetSource> {\n const resolved = resolveParquetUrl(input);\n if (!resolved) {\n throw new Error(\"Not a URL\");\n }\n\n const db = await getDuckDb();\n const conn = db.connect();\n const fileName = buildDuckDbFileName(resolved.url);\n\n db.registerFileURL(fileName, resolved.url, DuckDBDataProtocol.HTTP, true);\n\n return createParquetSource(db, conn, fileName);\n}\n\nexport async function openParquetSourceFromBuffer(buffer: Uint8Array): Promise<ParquetSource> {\n const db = await getDuckDb();\n const conn = db.connect();\n const fileName = buildDuckDbFileName(\"buffer\");\n\n db.registerFileBuffer(fileName, buffer);\n\n return createParquetSource(db, conn, fileName);\n}\n\nexport async function openParquetSource(input: string): Promise<ParquetSource> {\n const resolved = resolveParquetUrl(input);\n if (resolved) {\n return openParquetSourceFromUrl(input);\n }\n\n return openParquetSourceFromPath(input);\n}\n\nexport async function readParquetTableFromBuffer(\n buffer: Uint8Array,\n options?: ParquetReadOptions,\n): Promise<Table> {\n const source = await openParquetSourceFromBuffer(buffer);\n\n try {\n return await source.readTable(options);\n } finally {\n await source.close();\n }\n}\n\nexport async function readParquetTableFromPath(\n filePath: string,\n options?: ParquetReadOptions,\n): Promise<Table> {\n const source = await openParquetSourceFromPath(filePath);\n\n try {\n return await source.readTable(options);\n } finally {\n await source.close();\n }\n}\n\nexport async function readParquetTableFromUrl(\n input: string,\n options?: ParquetReadOptions,\n): Promise<Table> {\n const source = await openParquetSourceFromUrl(input);\n\n try {\n return await source.readTable(options);\n } finally {\n await source.close();\n }\n}\n\nexport async function readParquetMetadataFromBuffer(\n buffer: Uint8Array,\n): Promise<ParquetFileMetadata> {\n const source = await openParquetSourceFromBuffer(buffer);\n\n try {\n return await source.readMetadata();\n } finally {\n await source.close();\n }\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\nfunction createParquetSource(\n db: DuckDBBindings,\n conn: DuckDBConnection,\n fileName: string,\n): ParquetSource {\n let metadataPromise: Promise<ParquetFileMetadata> | null = null;\n\n return {\n readTable: async (options?: ParquetReadOptions) => {\n const query = buildSelectQuery(fileName, options);\n return conn.query(query);\n },\n readMetadata: () => {\n if (!metadataPromise) {\n metadataPromise = readParquetMetadata(conn, fileName);\n }\n return metadataPromise;\n },\n close: async () => {\n conn.close();\n db.dropFile(fileName);\n },\n };\n}\n\nfunction buildDuckDbFileName(input: string): string {\n const suffix = path.extname(input) || \".parquet\";\n return `parquetlens-${randomUUID()}${suffix}`;\n}\n\nfunction buildSelectQuery(fileName: string, options?: ParquetReadOptions): string {\n const columns = options?.columns && options.columns.length > 0 ? options.columns : null;\n const selectList = columns ? columns.map(quoteIdentifier).join(\", \") : \"*\";\n const limit = options?.limit;\n const offset = options?.offset;\n\n let query = `select ${selectList} from read_parquet(${quoteLiteral(fileName)})`;\n\n if (typeof limit === \"number\") {\n query += ` limit ${Math.max(0, limit)}`;\n }\n\n if (typeof offset === \"number\" && offset > 0) {\n query += ` offset ${Math.max(0, offset)}`;\n }\n\n return query;\n}\n\nfunction quoteIdentifier(value: string): string {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n}\n\nfunction quoteLiteral(value: string): string {\n return `'${value.replace(/'/g, \"''\")}'`;\n}\n\nasync function readParquetMetadata(\n conn: DuckDBConnection,\n fileName: string,\n): Promise<ParquetFileMetadata> {\n const metadataRows = tableToObjects(\n conn.query(`select * from parquet_file_metadata(${quoteLiteral(fileName)})`),\n );\n const kvRows = tableToObjects(\n conn.query(`select * from parquet_kv_metadata(${quoteLiteral(fileName)})`),\n );\n\n const createdByRaw = metadataRows[0]?.created_by ?? metadataRows[0]?.createdBy ?? null;\n const keyValueMetadata: Record<string, unknown> = {};\n\n for (const row of kvRows) {\n const key = row.key ?? row.key_name ?? row.name;\n if (typeof key !== \"string\" || key.length === 0) {\n continue;\n }\n keyValueMetadata[key] = row.value ?? row.val ?? \"\";\n }\n\n return {\n createdBy: normalizeMetadataValue(createdByRaw),\n keyValueMetadata: normalizeMetadataValues(keyValueMetadata),\n };\n}\n\nfunction tableToObjects(table: Table): Record<string, unknown>[] {\n const fields = table.schema.fields.map((field) => field.name);\n const rows: Record<string, unknown>[] = [];\n\n for (const batch of table.batches) {\n const vectors = fields.map((_, index) => batch.getChildAt(index));\n\n for (let rowIndex = 0; rowIndex < batch.numRows; rowIndex += 1) {\n const row: Record<string, unknown> = {};\n\n for (let colIndex = 0; colIndex < fields.length; colIndex += 1) {\n row[fields[colIndex]] = vectors[colIndex]?.get(rowIndex);\n }\n\n rows.push(row);\n }\n }\n\n return rows;\n}\n\nfunction normalizeMetadataValue(value: unknown): string | undefined {\n if (value === null || value === undefined) {\n return undefined;\n }\n\n if (value instanceof Uint8Array) {\n return Buffer.from(value).toString(\"utf8\");\n }\n\n if (typeof value === \"string\") {\n return value;\n }\n\n return String(value);\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 const normalizedValue = normalizeMetadataValue(value);\n normalized[key] = normalizedValue ?? \"\";\n }\n\n return normalized;\n}\n\nexport { resolveParquetUrl } from \"./urls.js\";\nexport type { ResolvedParquetUrl } from \"./urls.js\";\n","export type ResolvedParquetUrl = {\n url: string;\n};\n\nexport function resolveParquetUrl(input: string): ResolvedParquetUrl | null {\n if (input.startsWith(\"hf://\")) {\n return resolveHuggingFaceUrl(input);\n }\n\n if (input.startsWith(\"http://\") || input.startsWith(\"https://\")) {\n return { url: input };\n }\n\n return null;\n}\n\nfunction resolveHuggingFaceUrl(input: string): ResolvedParquetUrl {\n const match = input.match(/^hf:\\/\\/(datasets|models)\\/([^@\\/]+)\\/([^@\\/]+)(?:@([^\\/]+))?\\/(.+)$/);\n\n if (!match) {\n throw new Error(`Invalid hf:// URL: ${input}`);\n }\n\n const [, type, user, repo, branch = \"main\", filePath] = match;\n\n return {\n url: `https://huggingface.co/${type}/${user}/${repo}/resolve/${branch}/${filePath}`,\n };\n}\n"],"mappings":"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","names":["path"]}
package/dist/main.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/main.ts"],"sourcesContent":["#!/usr/bin/env node\nimport {\n ParquetReadOptions,\n readParquetTableFromPath,\n readParquetTableFromStdin,\n readParquetTableFromUrl,\n resolveParquetUrl,\n} from \"@parquetlens/parquet-reader\";\nimport type { Table } from \"apache-arrow\";\nimport { spawnSync } from \"node:child_process\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Polyfill __filename for ESM (tsx dev mode)\nconst __filename =\n typeof globalThis.__filename !== \"undefined\"\n ? globalThis.__filename\n : fileURLToPath(import.meta.url);\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|url|-> [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 hf://datasets/cfahlgren1/hub-stats/daily_papers.parquet\n parquetlens https://huggingface.co/datasets/cfahlgren1/hub-stats/resolve/main/daily_papers.parquet\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, URL, or pipe stdin)\");\n }\n\n if (source === \"-\") {\n return readParquetTableFromStdin(\"stdin.parquet\", readOptions);\n }\n\n if (resolveParquetUrl(source)) {\n return readParquetTableFromUrl(source, 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(\n \"parquetlens: tui mode requires a file path or URL (stdin not supported)\\n\",\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\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":[]}
package/dist/tui.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/tui.tsx"],"sourcesContent":["import { createCliRenderer } from \"@opentui/core\";\nimport { createRoot, useKeyboard, useTerminalDimensions } from \"@opentui/react\";\nimport { spawnSync } from \"node:child_process\";\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport type {\n ParquetSource,\n ParquetFileMetadata,\n ParquetReadOptions,\n} from \"@parquetlens/parquet-reader\";\nimport { openParquetSource } from \"@parquetlens/parquet-reader\";\n\ntype TuiOptions = {\n columns: string[];\n maxRows?: number;\n batchSize?: number;\n};\n\ntype ColumnInfo = {\n name: string;\n type: string;\n};\n\ntype GridState = {\n columns: ColumnInfo[];\n rows: string[][];\n};\n\ntype GridLines = {\n headerNameLine: string;\n headerTypeLine: string;\n separatorLine: string;\n rowLines: string[];\n maxLineLength: number;\n columnRanges: Array<{ start: number; end: number }>;\n scrollStops: number[];\n};\n\nconst TOP_BAR_LINES = 3;\nconst FOOTER_LINES = 4;\nconst TABLE_BORDER_LINES = 2;\nconst TABLE_HEADER_LINES = 3;\nconst RESERVED_LINES = TOP_BAR_LINES + FOOTER_LINES + TABLE_BORDER_LINES + TABLE_HEADER_LINES;\nconst DEFAULT_COLUMN_WIDTH = 6;\nconst MAX_COLUMN_WIDTH = 40;\nconst SCROLL_STEP = 3;\nconst SIDEBAR_WIDTH_RATIO = 0.35;\nconst SIDEBAR_MIN_WIDTH = 24;\nconst CONTENT_BORDER_WIDTH = 2;\nconst PANEL_GAP = 1;\n\nconst THEME = {\n background: \"#1e1f29\",\n panel: \"#22232e\",\n header: \"#2d2f3d\",\n border: \"#44475a\",\n accent: \"#bd93f9\",\n badge: \"#50fa7b\",\n badgeText: \"#1e1f29\",\n text: \"#c5cee0\",\n muted: \"#6272a4\",\n stripe: \"#252733\",\n};\n\nexport async function runTui(input: string, options: TuiOptions): Promise<void> {\n const source = await openParquetSource(input);\n const renderer = await createCliRenderer({\n exitOnCtrlC: true,\n useAlternateScreen: true,\n useConsole: false,\n useMouse: true,\n enableMouseMovement: true,\n });\n renderer.setTerminalTitle(\"parquetlens\");\n const root = createRoot(renderer);\n\n const handleExit = () => {\n root.unmount();\n renderer.destroy();\n };\n\n root.render(<App source={source} filePath={input} options={options} onExit={handleExit} />);\n}\n\ntype AppProps = {\n source: ParquetSource;\n filePath: string;\n options: TuiOptions;\n onExit: () => void;\n};\n\nfunction App({ source, filePath, options, onExit }: AppProps) {\n const { width, height } = useTerminalDimensions();\n const pageSize = Math.max(1, height - RESERVED_LINES);\n\n const [offset, setOffset] = useState(0);\n const [xOffset, setXOffset] = useState(0);\n const [grid, setGrid] = useState<GridState>({ columns: [], rows: [] } as GridState);\n const [selection, setSelection] = useState<{ row: number; col: number } | null>(null);\n const [sidebarOpen, setSidebarOpen] = useState(false);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [notice, setNotice] = useState<string | null>(null);\n const [metadata, setMetadata] = useState<ParquetFileMetadata | null>(null);\n const [knownTotalRows, setKnownTotalRows] = useState<number | null>(null);\n const noticeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const effectiveTotal = options.maxRows ?? knownTotalRows;\n const maxOffset =\n effectiveTotal === undefined || effectiveTotal === null\n ? undefined\n : Math.max(0, effectiveTotal - pageSize);\n const sidebarWidth = sidebarOpen\n ? Math.min(width, Math.max(SIDEBAR_MIN_WIDTH, Math.floor(width * SIDEBAR_WIDTH_RATIO)))\n : 0;\n const tableWidth = Math.max(0, width - (sidebarOpen ? sidebarWidth + PANEL_GAP : 0));\n const tableContentWidth = Math.max(0, tableWidth - CONTENT_BORDER_WIDTH);\n\n const columnsToRead = options.columns;\n\n useEffect(() => {\n return () => {\n void source.close();\n };\n }, [source]);\n\n useEffect(() => {\n let canceled = false;\n\n const loadWindow = async () => {\n setLoading(true);\n setError(null);\n try {\n const limit = options.maxRows\n ? Math.max(0, Math.min(pageSize, options.maxRows - offset))\n : pageSize;\n const readOptions: ParquetReadOptions = {\n batchSize: options.batchSize ?? 1024,\n columns: columnsToRead.length > 0 ? columnsToRead : undefined,\n limit,\n offset,\n };\n const table = await source.readTable(readOptions);\n const columns: ColumnInfo[] = table.schema.fields.map((field) => ({\n name: field.name,\n type: formatArrowType(field.type),\n }));\n const rows = tableToRows(\n table,\n columns.map((c) => c.name),\n );\n\n if (!canceled) {\n setGrid({ columns, rows });\n // Detect end of file: if we got fewer rows than requested, we know the total\n if (rows.length < limit) {\n setKnownTotalRows(offset + rows.length);\n }\n }\n } catch (caught) {\n const message = caught instanceof Error ? caught.message : String(caught);\n if (!canceled) {\n setError(message);\n }\n } finally {\n if (!canceled) {\n setLoading(false);\n }\n }\n };\n\n loadWindow();\n\n return () => {\n canceled = true;\n };\n }, [columnsToRead, offset, options.batchSize, options.maxRows, pageSize, source]);\n\n useEffect(() => {\n let canceled = false;\n source\n .readMetadata()\n .then((meta) => {\n if (!canceled) {\n setMetadata(meta);\n }\n })\n .catch(() => {\n if (!canceled) {\n setMetadata(null);\n }\n });\n\n return () => {\n canceled = true;\n };\n }, [source]);\n\n const gridLines = useMemo(\n () => buildGridLines(grid, offset, tableContentWidth),\n [grid, offset, tableContentWidth],\n );\n const maxScrollX = Math.max(0, gridLines.maxLineLength - tableContentWidth);\n\n useEffect(() => {\n setXOffset((current) => Math.min(current, maxScrollX));\n }, [gridLines.maxLineLength, maxScrollX, tableContentWidth]);\n\n useEffect(() => {\n if (grid.rows.length === 0 || grid.columns.length === 0) {\n setSelection(null);\n return;\n }\n\n setSelection((current) => {\n const nextRow = current ? Math.min(current.row, grid.rows.length - 1) : 0;\n const nextCol = current ? Math.min(current.col, grid.columns.length - 1) : 0;\n return { row: nextRow, col: nextCol };\n });\n }, [grid.columns.length, grid.rows.length]);\n\n useEffect(() => {\n if (error) {\n setSidebarOpen(true);\n }\n }, [error]);\n\n useEffect(() => {\n return () => {\n if (noticeTimer.current) {\n clearTimeout(noticeTimer.current);\n }\n };\n }, []);\n\n const showNotice = (message: string) => {\n setNotice(message);\n if (noticeTimer.current) {\n clearTimeout(noticeTimer.current);\n }\n noticeTimer.current = setTimeout(() => {\n setNotice(null);\n }, 2000);\n };\n\n useKeyboard((key) => {\n if ((key.ctrl && key.name === \"c\") || key.name === \"escape\" || key.name === \"q\") {\n if (sidebarOpen && key.name === \"escape\") {\n setSidebarOpen(false);\n return;\n }\n onExit();\n return;\n }\n\n const clampOffset = (value: number) => {\n const clamped = Math.max(0, value);\n return maxOffset === undefined ? clamped : Math.min(clamped, maxOffset);\n };\n\n if (key.name === \"down\" || key.name === \"j\") {\n setOffset((current) => clampOffset(current + 1));\n return;\n }\n\n if (key.name === \"up\" || key.name === \"k\") {\n setOffset((current) => clampOffset(current - 1));\n return;\n }\n\n if (key.name === \"pagedown\" || key.name === \"space\") {\n setOffset((current) => clampOffset(current + pageSize));\n return;\n }\n\n if (key.name === \"pageup\") {\n setOffset((current) => clampOffset(current - pageSize));\n return;\n }\n\n if (key.name === \"home\" || (key.name === \"g\" && !key.shift)) {\n setOffset(0);\n return;\n }\n\n if ((key.name === \"end\" || (key.name === \"g\" && key.shift)) && maxOffset !== undefined) {\n setOffset(maxOffset);\n return;\n }\n\n if (key.name === \"return\" || key.name === \"enter\") {\n setSidebarOpen((current) => !current);\n return;\n }\n\n if (key.name === \"x\") {\n setSidebarOpen(false);\n return;\n }\n\n if (key.name === \"s\") {\n setSidebarOpen((current) => !current);\n return;\n }\n\n if (key.name === \"e\" && error) {\n setSidebarOpen(true);\n return;\n }\n\n if (key.name === \"y\" && error) {\n const copied = copyToClipboard(error);\n showNotice(copied ? \"copied error to clipboard\" : \"clipboard unavailable\");\n return;\n }\n\n if (key.name === \"left\" || key.name === \"h\") {\n setXOffset((current) =>\n clampScroll(findScrollStop(current, gridLines.scrollStops, -1), maxScrollX),\n );\n return;\n }\n\n if (key.name === \"right\" || key.name === \"l\") {\n setXOffset((current) =>\n clampScroll(findScrollStop(current, gridLines.scrollStops, 1), maxScrollX),\n );\n }\n });\n\n const visibleLines = applyHorizontalScroll(gridLines, tableContentWidth, xOffset, pageSize);\n const detail = error ? buildErrorDetail(error) : buildDetail(selection, grid, offset);\n const detailTitle = error ? \"error detail\" : \"cell detail\";\n const metaFlags = getMetadataFlags(metadata);\n\n return (\n <box flexDirection=\"column\" width=\"100%\" height=\"100%\" backgroundColor={THEME.background}>\n <box backgroundColor={THEME.header} border borderColor={THEME.border}>\n {renderHeader({\n filePath,\n offset,\n rows: grid.rows.length,\n columns: grid.columns.length,\n loading,\n error,\n maxRows: effectiveTotal ?? undefined,\n optimized: metaFlags.optimized,\n createdBy: metaFlags.createdBy,\n })}\n </box>\n <box flexGrow={1} flexDirection=\"row\" gap={PANEL_GAP}>\n <box\n backgroundColor={THEME.panel}\n border\n borderColor={THEME.border}\n flexGrow={1}\n width={sidebarOpen ? tableWidth : \"100%\"}\n onMouseScroll={(event) => {\n if (!event.scroll) {\n return;\n }\n\n const delta = Math.max(1, event.scroll.delta);\n const step = delta * SCROLL_STEP;\n\n if (event.scroll.direction === \"up\") {\n setOffset((current) => Math.max(0, current - step));\n } else if (event.scroll.direction === \"down\") {\n setOffset((current) => {\n const next = current + step;\n return maxOffset === undefined ? next : Math.min(next, maxOffset);\n });\n } else if (event.scroll.direction === \"left\") {\n setXOffset((current) => clampScroll(current - step, maxScrollX));\n } else if (event.scroll.direction === \"right\") {\n setXOffset((current) => clampScroll(current + step, maxScrollX));\n }\n }}\n >\n <text wrapMode=\"none\" truncate fg={THEME.accent}>\n {visibleLines.headerName}\n </text>\n <text wrapMode=\"none\" truncate fg={THEME.muted}>\n {visibleLines.headerType}\n </text>\n <text wrapMode=\"none\" truncate fg={THEME.border}>\n {visibleLines.separator}\n </text>\n {visibleLines.rows.map((line, index) => {\n const isSelected = selection?.row === index;\n return (\n <text\n key={`row-${index}`}\n wrapMode=\"none\"\n truncate\n fg={THEME.text}\n bg={isSelected ? THEME.header : index % 2 === 0 ? THEME.background : THEME.stripe}\n onMouseDown={(event) => {\n const target = event.target;\n if (!target) {\n return;\n }\n const localX = Math.max(0, event.x - target.x);\n const absoluteX = localX + xOffset;\n const colIndex = findColumnIndex(absoluteX, gridLines.columnRanges);\n if (colIndex >= 0) {\n setSelection({ row: index, col: colIndex });\n setSidebarOpen(true);\n }\n }}\n >\n {line}\n </text>\n );\n })}\n </box>\n {sidebarOpen ? (\n <box\n width=\"35%\"\n minWidth={24}\n backgroundColor={THEME.panel}\n border\n borderColor={THEME.border}\n title={detailTitle}\n titleAlignment=\"left\"\n >\n <text wrapMode=\"none\" truncate fg={THEME.muted}>\n press esc/x to close\n </text>\n <scrollbox scrollY flexGrow={1} backgroundColor={THEME.panel}>\n <text\n wrapMode=\"word\"\n fg={THEME.text}\n selectable\n selectionBg={THEME.accent}\n selectionFg={THEME.background}\n >\n {detail}\n </text>\n </scrollbox>\n <text\n wrapMode=\"none\"\n truncate\n fg={THEME.accent}\n onMouseDown={() => {\n setSidebarOpen(false);\n }}\n >\n [ close ]\n </text>\n </box>\n ) : null}\n </box>\n <box backgroundColor={THEME.header} border borderColor={THEME.border}>\n {renderFooter(Boolean(error), notice)}\n </box>\n </box>\n );\n}\n\ntype HeaderProps = {\n filePath: string;\n offset: number;\n rows: number;\n columns: number;\n loading: boolean;\n error: string | null;\n maxRows?: number;\n createdBy?: string;\n optimized: boolean;\n};\n\nfunction renderHeader(props: HeaderProps) {\n const { filePath, offset, rows, columns, loading, error, maxRows, createdBy, optimized } = props;\n\n const start = rows > 0 ? offset + 1 : offset;\n const end = rows > 0 ? offset + rows : offset;\n const totalText = maxRows !== undefined ? `of ${maxRows.toLocaleString()}` : \"\";\n const fileName = filePath.split(\"/\").pop() ?? filePath;\n\n if (error) {\n return (\n <box flexDirection=\"row\" alignItems=\"center\" gap={2} width=\"100%\">\n <text wrapMode=\"none\" fg={THEME.accent}>\n {\"◈ parquetlens\"}\n </text>\n <text wrapMode=\"none\" fg=\"#ef4444\">\n {\"error: \" + error}\n </text>\n </box>\n );\n }\n\n return (\n <box flexDirection=\"row\" alignItems=\"center\" gap={2} width=\"100%\">\n <text wrapMode=\"none\" fg={THEME.accent}>\n {\"◈ parquetlens\"}\n </text>\n <text wrapMode=\"none\" fg={THEME.muted}>\n {\"│\"}\n </text>\n <text wrapMode=\"none\" fg={THEME.text}>\n {fileName}\n </text>\n <text wrapMode=\"none\" fg={THEME.muted}>\n {\"│\"}\n </text>\n <text wrapMode=\"none\" fg={THEME.muted}>\n {\"rows \"}\n </text>\n <text wrapMode=\"none\" fg={THEME.text}>\n {`${start.toLocaleString()}-${end.toLocaleString()} ${totalText}`}\n </text>\n <text wrapMode=\"none\" fg={THEME.muted}>\n {\"│\"}\n </text>\n <text wrapMode=\"none\" fg={THEME.muted}>\n {\"cols \"}\n </text>\n <text wrapMode=\"none\" fg={THEME.text}>\n {columns.toString()}\n </text>\n {createdBy ? (\n <>\n <text wrapMode=\"none\" fg={THEME.muted}>\n {\"│\"}\n </text>\n <text wrapMode=\"none\" fg={THEME.muted} truncate>\n {createdBy}\n </text>\n </>\n ) : null}\n <box flexGrow={1} />\n {loading ? (\n <text wrapMode=\"none\" fg={THEME.badge}>\n {\"● loading\"}\n </text>\n ) : null}\n {optimized ? (\n <text wrapMode=\"none\" fg={THEME.badgeText} bg={THEME.badge}>\n {\" ✓ OPTIMIZED \"}\n </text>\n ) : null}\n </box>\n );\n}\n\nfunction renderFooterLine(hasError: boolean): string {\n const errorHint = hasError ? \" | e view error | y copy error\" : \"\";\n return `q exit | arrows/jk scroll | pgup/pgdn page | h/l col jump | mouse wheel scroll | click cell for detail | s/enter toggle panel${errorHint}`;\n}\n\nfunction renderFooter(hasError: boolean, notice: string | null) {\n const controls = renderFooterLine(hasError);\n\n return (\n <box flexDirection=\"column\" width=\"100%\">\n {notice ? (\n <text wrapMode=\"none\" truncate fg={THEME.badge}>\n {notice}\n </text>\n ) : null}\n <text wrapMode=\"none\" truncate fg={THEME.muted}>\n {controls}\n </text>\n </box>\n );\n}\n\nfunction buildGridLines(grid: GridState, offset: number, targetWidth: number): GridLines {\n const columns: ColumnInfo[] =\n grid.columns.length > 0 ? grid.columns : [{ name: \"(loading)\", type: \"\" }];\n const rows = grid.rows;\n\n const rowNumberWidth = Math.max(String(offset + rows.length).length, 3);\n const columnWidths = columns.map((col, index) => {\n const longestCell = rows.reduce(\n (max, row) => {\n const value = row[index] ?? \"\";\n return Math.max(max, value.length);\n },\n Math.max(col.name.length, col.type.length),\n );\n return Math.min(Math.max(longestCell, DEFAULT_COLUMN_WIDTH), MAX_COLUMN_WIDTH);\n });\n\n const headerNames = [\"#\", ...columns.map((c) => c.name)];\n const headerTypes = [\"\", ...columns.map((c) => c.type)];\n const headerWidths = [rowNumberWidth, ...columnWidths];\n const separatorWidth = headerWidths.length > 1 ? (headerWidths.length - 1) * 2 : 0;\n const baseLength = headerWidths.reduce((total, width) => total + width, 0) + separatorWidth;\n\n if (targetWidth > baseLength && headerWidths.length > 1) {\n headerWidths[headerWidths.length - 1] += targetWidth - baseLength;\n }\n const headerNameLine = buildLine(headerNames, headerWidths);\n const headerTypeLine = buildLine(headerTypes, headerWidths);\n const separatorLine = buildSeparator(headerWidths);\n const rowLines = rows.map((row, index) => {\n const rowIndex = String(offset + index + 1);\n const values = [rowIndex, ...row];\n return buildLine(values, headerWidths);\n });\n\n const { columnRanges, scrollStops } = buildColumnRanges(headerWidths);\n\n const maxLineLength = Math.max(\n headerNameLine.length,\n headerTypeLine.length,\n separatorLine.length,\n ...rowLines.map((line) => line.length),\n );\n\n return {\n headerNameLine,\n headerTypeLine,\n separatorLine,\n rowLines,\n maxLineLength,\n columnRanges,\n scrollStops,\n };\n}\n\nfunction applyHorizontalScroll(\n lines: GridLines,\n width: number,\n xOffset: number,\n pageSize: number,\n): { headerName: string; headerType: string; separator: string; rows: string[] } {\n const sliceLine = (line: string) => {\n if (width <= 0) {\n return \"\";\n }\n const sliced = xOffset <= 0 ? line.slice(0, width) : line.slice(xOffset, xOffset + width);\n return sliced.padEnd(width, \" \");\n };\n\n const rows = lines.rowLines.slice(0, pageSize).map(sliceLine);\n const emptyRow = width > 0 ? \" \".repeat(width) : \"\";\n while (rows.length < pageSize) {\n rows.push(emptyRow);\n }\n\n return {\n headerName: sliceLine(lines.headerNameLine),\n headerType: sliceLine(lines.headerTypeLine),\n separator: sliceLine(lines.separatorLine),\n rows,\n };\n}\n\nfunction buildLine(values: string[], widths: number[]): string {\n return values\n .map((value, index) => {\n const width = widths[index] ?? DEFAULT_COLUMN_WIDTH;\n return padCell(value, width);\n })\n .join(\" \");\n}\n\nfunction buildSeparator(widths: number[]): string {\n return \"\";\n}\n\nfunction padCell(value: string, width: number): string {\n const normalized = normalizeCell(value);\n if (normalized.length > width) {\n if (width <= 3) {\n return normalized.slice(0, width);\n }\n return `${normalized.slice(0, width - 3)}...`;\n }\n return normalized.padEnd(width, \" \");\n}\n\nfunction normalizeCell(value: string): string {\n return value.replace(/\\r?\\n/g, \"\\\\n\").replace(/\\t/g, \"\\\\t\");\n}\n\nfunction buildColumnRanges(widths: number[]): {\n columnRanges: Array<{ start: number; end: number }>;\n scrollStops: number[];\n} {\n const columnRanges: Array<{ start: number; end: number }> = [];\n const scrollStops: number[] = [0];\n let cursor = 0;\n\n for (let i = 0; i < widths.length; i += 1) {\n const width = widths[i];\n if (i > 0) {\n const start = cursor;\n const end = cursor + width - 1;\n columnRanges.push({ start, end });\n scrollStops.push(start);\n }\n cursor += width;\n if (i < widths.length - 1) {\n cursor += 2;\n }\n }\n\n return { columnRanges, scrollStops };\n}\n\nfunction findColumnIndex(x: number, ranges: Array<{ start: number; end: number }>): number {\n for (let index = 0; index < ranges.length; index += 1) {\n const range = ranges[index];\n if (x >= range.start && x <= range.end) {\n return index;\n }\n }\n return -1;\n}\n\nfunction findScrollStop(current: number, stops: number[], direction: 1 | -1): number {\n if (direction > 0) {\n for (const stop of stops) {\n if (stop > current) {\n return stop;\n }\n }\n return current;\n }\n\n for (let index = stops.length - 1; index >= 0; index -= 1) {\n const stop = stops[index];\n if (stop < current) {\n return stop;\n }\n }\n\n return 0;\n}\n\nfunction clampScroll(value: number, max: number): number {\n if (value < 0) {\n return 0;\n }\n if (value > max) {\n return max;\n }\n return value;\n}\n\nfunction buildDetail(\n selection: { row: number; col: number } | null,\n grid: GridState,\n offset: number,\n): string {\n if (!selection || grid.columns.length === 0 || grid.rows.length === 0) {\n return \"click a cell to see full details\";\n }\n\n const rowIndex = Math.min(selection.row, grid.rows.length - 1);\n const colIndex = Math.min(selection.col, grid.columns.length - 1);\n const col = grid.columns[colIndex];\n const columnName = col?.name ?? \"(unknown)\";\n const columnType = col?.type ?? \"\";\n const value = grid.rows[rowIndex]?.[colIndex] ?? \"\";\n const absoluteRow = offset + rowIndex + 1;\n\n return `row ${absoluteRow} • ${columnName}\\n${columnType}\\n\\n${value}`;\n}\n\nfunction buildErrorDetail(message: string): string {\n return `error\\n\\n${message}`;\n}\n\nfunction getMetadataFlags(metadata: ParquetFileMetadata | null): {\n optimized: boolean;\n createdBy?: string;\n} {\n if (!metadata) {\n return { optimized: false };\n }\n\n const raw = metadata.keyValueMetadata[\"content_defined_chunking\"];\n const normalized = raw?.toLowerCase?.() ?? \"\";\n const optimized =\n raw !== undefined && raw !== null && normalized !== \"false\" && normalized !== \"0\";\n\n return {\n optimized,\n createdBy: metadata.createdBy,\n };\n}\n\nfunction tableToRows(table: import(\"apache-arrow\").Table, columns: string[]): string[][] {\n const rows: string[][] = [];\n\n for (const batch of table.batches) {\n const vectors = columns.map((_, index) => batch.getChildAt(index));\n\n for (let rowIndex = 0; rowIndex < batch.numRows; rowIndex += 1) {\n const row = vectors.map((vector) => formatCell(vector?.get(rowIndex)));\n rows.push(row);\n }\n }\n\n return rows;\n}\n\nfunction formatCell(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\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 String(value);\n}\n\nfunction formatArrowType(type: import(\"apache-arrow\").DataType): string {\n return type.toString();\n}\n\nfunction copyToClipboard(value: string): boolean {\n const platform = process.platform;\n const candidates: Array<[string, string[]]> = [];\n\n if (platform === \"darwin\") {\n candidates.push([\"pbcopy\", []]);\n } else if (platform === \"win32\") {\n candidates.push([\"clip\", []]);\n } else {\n candidates.push([\"wl-copy\", []], [\"xclip\", [\"-selection\", \"clipboard\"]]);\n }\n\n for (const [command, args] of candidates) {\n const result = spawnSync(command, args, { input: value });\n if (!result.error && result.status === 0) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":"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","names":[]}