simple-ascii-chart-cli 2.2.0 → 3.0.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.
package/dist/cli.js CHANGED
@@ -1,16 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __assign = (this && this.__assign) || function () {
4
- __assign = Object.assign || function(t) {
5
- for (var s, i = 1, n = arguments.length; i < n; i++) {
6
- s = arguments[i];
7
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
8
- t[p] = s[p];
9
- }
10
- return t;
11
- };
12
- return __assign.apply(this, arguments);
13
- };
14
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
4
  if (k2 === undefined) k2 = k;
16
5
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -48,22 +37,583 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
48
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
49
38
  };
50
39
  Object.defineProperty(exports, "__esModule", { value: true });
51
- // Import necessary modules and types
52
- var yargs = __importStar(require("yargs"));
53
- var simple_ascii_chart_1 = __importDefault(require("simple-ascii-chart"));
54
- var validators_1 = require("./validators");
55
- // Define command-line arguments with yargs
56
- var argv = yargs
40
+ const fs = __importStar(require("node:fs/promises"));
41
+ const path = __importStar(require("node:path"));
42
+ const readline = __importStar(require("node:readline"));
43
+ const yargs = __importStar(require("yargs"));
44
+ const simple_ascii_chart_1 = __importDefault(require("simple-ascii-chart"));
45
+ const options_1 = require("./options");
46
+ const clearScreen = '\x1b[2J\x1b[H';
47
+ class CliError extends Error {
48
+ cause;
49
+ constructor(message, cause) {
50
+ super(message);
51
+ this.cause = cause;
52
+ this.name = 'CliError';
53
+ }
54
+ }
55
+ const fail = (message, cause) => {
56
+ throw new CliError(message, cause);
57
+ };
58
+ const parseNumber = (value) => {
59
+ const parsed = Number(value.trim());
60
+ return Number.isFinite(parsed) ? parsed : undefined;
61
+ };
62
+ const formatElapsedAxisTick = (elapsedMilliseconds) => {
63
+ const elapsedSeconds = Math.max(0, Math.round(elapsedMilliseconds / 1000));
64
+ if (elapsedSeconds < 60) {
65
+ return `+${elapsedSeconds}s`;
66
+ }
67
+ const minutes = Math.floor(elapsedSeconds / 60);
68
+ const seconds = elapsedSeconds % 60;
69
+ if (minutes < 60) {
70
+ return `+${minutes}m${seconds}s`;
71
+ }
72
+ const hours = Math.floor(minutes / 60);
73
+ const remainingMinutes = minutes % 60;
74
+ return `+${hours}h${remainingMinutes}m`;
75
+ };
76
+ const inferFormatFromFilePath = (inputFile) => {
77
+ const extension = path.extname(inputFile).toLowerCase();
78
+ if (extension === '.csv')
79
+ return 'csv';
80
+ if (extension === '.tsv' || extension === '.tab')
81
+ return 'tsv';
82
+ if (extension === '.space' || extension === '.dat' || extension === '.txt')
83
+ return 'space';
84
+ return 'json';
85
+ };
86
+ const inferFormatFromRawInput = (raw) => {
87
+ const trimmed = raw.trim();
88
+ if (!trimmed) {
89
+ return 'json';
90
+ }
91
+ if (trimmed.startsWith('[') || trimmed.startsWith('{')) {
92
+ return 'json';
93
+ }
94
+ const firstLine = trimmed.split(/\r?\n/, 1)[0];
95
+ if (firstLine.includes('\t')) {
96
+ return 'tsv';
97
+ }
98
+ if (firstLine.includes(',')) {
99
+ return 'csv';
100
+ }
101
+ return 'space';
102
+ };
103
+ const getInputFormat = ({ explicit, inputFile, raw, }) => {
104
+ if (explicit) {
105
+ return explicit;
106
+ }
107
+ if (inputFile) {
108
+ return inferFormatFromFilePath(inputFile);
109
+ }
110
+ return inferFormatFromRawInput(raw);
111
+ };
112
+ const isPoint = (value) => {
113
+ return (Array.isArray(value) &&
114
+ value.length === 2 &&
115
+ typeof value[0] === 'number' &&
116
+ Number.isFinite(value[0]) &&
117
+ typeof value[1] === 'number' &&
118
+ Number.isFinite(value[1]));
119
+ };
120
+ const parseJsonCoordinates = (raw, sourceLabel) => {
121
+ let parsedUnknown;
122
+ try {
123
+ parsedUnknown = JSON.parse(raw);
124
+ }
125
+ catch (error) {
126
+ const details = error instanceof Error ? error.message : String(error);
127
+ return fail(`Invalid ${sourceLabel} JSON: ${details}`);
128
+ }
129
+ if (!Array.isArray(parsedUnknown)) {
130
+ fail(`Invalid ${sourceLabel}: expected an array`);
131
+ }
132
+ const parsed = parsedUnknown;
133
+ if (parsed.length === 0) {
134
+ return [];
135
+ }
136
+ if (parsed.every((item) => isPoint(item))) {
137
+ return parsed;
138
+ }
139
+ if (parsed.every((series) => Array.isArray(series) && series.every((item) => isPoint(item)))) {
140
+ return parsed;
141
+ }
142
+ return fail(`Invalid ${sourceLabel}: expected [[x,y], ...] or [[[x,y], ...], ...]`);
143
+ };
144
+ const splitDelimitedLine = (line, format, delimiter) => {
145
+ if (delimiter !== undefined) {
146
+ return line.split(delimiter);
147
+ }
148
+ if (format === 'csv') {
149
+ return line.split(',');
150
+ }
151
+ if (format === 'tsv') {
152
+ return line.split('\t');
153
+ }
154
+ return line.trim().split(/\s+/);
155
+ };
156
+ const resolveColumnIndex = ({ column, defaultIndex, axis, headerColumns, }) => {
157
+ if (column === undefined) {
158
+ return defaultIndex;
159
+ }
160
+ if (/^\d+$/.test(column)) {
161
+ const index = Number(column) - 1;
162
+ if (index < 0) {
163
+ fail(`--${axis}-col must be a positive 1-based index`);
164
+ }
165
+ return index;
166
+ }
167
+ if (!headerColumns) {
168
+ return fail(`--${axis}-col="${column}" requires --header or a numeric column index`);
169
+ }
170
+ const index = headerColumns.indexOf(column);
171
+ if (index === -1) {
172
+ fail(`--${axis}-col="${column}" was not found in the header row`);
173
+ }
174
+ return index;
175
+ };
176
+ const parseDelimitedCoordinates = ({ raw, format, delimiter, header, xCol, yCol, sourceLabel, }) => {
177
+ const lines = raw
178
+ .split(/\r?\n/)
179
+ .map((line) => line.trim())
180
+ .filter((line) => line.length > 0);
181
+ if (lines.length === 0) {
182
+ fail(`No data found in ${sourceLabel}`);
183
+ }
184
+ const rows = lines.map((line) => splitDelimitedLine(line, format, delimiter));
185
+ let startIndex = 0;
186
+ let headerColumns;
187
+ if (header) {
188
+ headerColumns = rows[0].map((column) => column.trim());
189
+ startIndex = 1;
190
+ }
191
+ const xIndex = resolveColumnIndex({
192
+ column: xCol,
193
+ defaultIndex: 0,
194
+ axis: 'x',
195
+ headerColumns,
196
+ });
197
+ const yIndex = resolveColumnIndex({
198
+ column: yCol,
199
+ defaultIndex: 1,
200
+ axis: 'y',
201
+ headerColumns,
202
+ });
203
+ const points = [];
204
+ for (let index = startIndex; index < rows.length; index += 1) {
205
+ const row = rows[index];
206
+ const x = parseNumber(row[xIndex] ?? '');
207
+ const y = parseNumber(row[yIndex] ?? '');
208
+ if (x !== undefined && y !== undefined) {
209
+ points.push([x, y]);
210
+ }
211
+ }
212
+ if (points.length === 0) {
213
+ fail(`No valid numeric points could be parsed from ${sourceLabel}`);
214
+ }
215
+ return points;
216
+ };
217
+ const parseCoordinatesFromRaw = ({ raw, args, sourceLabel, inputFile, }) => {
218
+ const format = getInputFormat({
219
+ explicit: args.format,
220
+ inputFile,
221
+ raw,
222
+ });
223
+ if (format === 'json') {
224
+ return parseJsonCoordinates(raw, sourceLabel);
225
+ }
226
+ return parseDelimitedCoordinates({
227
+ raw,
228
+ format,
229
+ delimiter: args.delimiter,
230
+ header: args.header,
231
+ xCol: args.xCol,
232
+ yCol: args.yCol,
233
+ sourceLabel,
234
+ });
235
+ };
236
+ const readStdinText = async () => {
237
+ return new Promise((resolve, reject) => {
238
+ let output = '';
239
+ process.stdin.setEncoding('utf8');
240
+ process.stdin.on('data', (chunk) => {
241
+ output += chunk;
242
+ });
243
+ process.stdin.on('error', reject);
244
+ process.stdin.on('end', () => resolve(output));
245
+ });
246
+ };
247
+ const getStaticInput = async (args) => {
248
+ if (args.input !== undefined) {
249
+ if (!args.input.trim()) {
250
+ fail('`--input` was provided but empty. Pass coordinate data or use --input-file/stdin.');
251
+ }
252
+ return parseCoordinatesFromRaw({
253
+ raw: args.input,
254
+ args,
255
+ sourceLabel: '--input',
256
+ });
257
+ }
258
+ if (args.inputFile) {
259
+ try {
260
+ const content = await fs.readFile(args.inputFile, 'utf8');
261
+ return parseCoordinatesFromRaw({
262
+ raw: content,
263
+ args,
264
+ sourceLabel: `file ${args.inputFile}`,
265
+ inputFile: args.inputFile,
266
+ });
267
+ }
268
+ catch (error) {
269
+ const details = error instanceof Error ? error.message : String(error);
270
+ fail(`Unable to read --input-file "${args.inputFile}": ${details}`);
271
+ }
272
+ }
273
+ if (!process.stdin.isTTY) {
274
+ const stdinContent = await readStdinText();
275
+ if (!stdinContent.trim()) {
276
+ fail('Stdin is empty. Pipe data or provide --input/--input-file.');
277
+ }
278
+ return parseCoordinatesFromRaw({
279
+ raw: stdinContent,
280
+ args,
281
+ sourceLabel: 'stdin',
282
+ });
283
+ }
284
+ return fail('Missing input. Provide --input, --input-file, or pipe data via stdin.');
285
+ };
286
+ const parseStreamSample = ({ line, lastAutoX, seriesCount, }) => {
287
+ const trimmed = line.trim();
288
+ if (!trimmed) {
289
+ return { nextAutoX: lastAutoX };
290
+ }
291
+ const tokens = trimmed.split(/[,\s]+/).filter(Boolean);
292
+ const withAutoX = seriesCount;
293
+ const withExplicitX = seriesCount + 1;
294
+ const parseValues = (startIndex) => {
295
+ const values = tokens
296
+ .slice(startIndex)
297
+ .map((token) => parseNumber(token))
298
+ .filter((value) => value !== undefined);
299
+ if (values.length !== seriesCount) {
300
+ return undefined;
301
+ }
302
+ return values;
303
+ };
304
+ const sampleMs = Date.now();
305
+ if (tokens.length === withAutoX) {
306
+ const values = parseValues(0);
307
+ if (!values) {
308
+ return { nextAutoX: lastAutoX };
309
+ }
310
+ const x = sampleMs > lastAutoX ? sampleMs : lastAutoX + 1;
311
+ return {
312
+ x,
313
+ values,
314
+ sampleMs,
315
+ nextAutoX: x,
316
+ };
317
+ }
318
+ if (tokens.length === withExplicitX) {
319
+ const x = parseNumber(tokens[0]);
320
+ const values = parseValues(1);
321
+ if (x === undefined || !values) {
322
+ return { nextAutoX: lastAutoX };
323
+ }
324
+ return {
325
+ x,
326
+ values,
327
+ sampleMs,
328
+ nextAutoX: x > lastAutoX ? x : lastAutoX,
329
+ };
330
+ }
331
+ return { nextAutoX: lastAutoX };
332
+ };
333
+ const executeStatic = ({ input, options, plotOutput, }) => {
334
+ const output = (0, simple_ascii_chart_1.default)(input, options);
335
+ const stream = plotOutput === 'stderr' ? process.stderr : process.stdout;
336
+ stream.write(output);
337
+ };
338
+ const executeStream = ({ options, plotOutput, refreshMs, seriesCount, rateEnabled, window, passthrough, }) => {
339
+ const plotStream = plotOutput === 'stderr' ? process.stderr : process.stdout;
340
+ const seriesPoints = Array.from({ length: seriesCount }, () => []);
341
+ let nextAutoX = Date.now();
342
+ let streamStartX;
343
+ let previousRateSample;
344
+ let lastRenderAt = 0;
345
+ let hasPendingRender = false;
346
+ let renderTimer;
347
+ let isFinished = false;
348
+ const streamOptions = {
349
+ ...(options ?? {}),
350
+ };
351
+ if (seriesCount === 2 && streamOptions.color === undefined) {
352
+ streamOptions.color = ['ansiCyan', 'ansiYellow'];
353
+ }
354
+ if (!streamOptions.formatter) {
355
+ streamOptions.formatter = (value, helpers) => {
356
+ if (helpers.axis === 'x') {
357
+ const startX = streamStartX ?? value;
358
+ return formatElapsedAxisTick(value - startX);
359
+ }
360
+ return value;
361
+ };
362
+ }
363
+ const render = () => {
364
+ if (seriesPoints[0].length === 0) {
365
+ return;
366
+ }
367
+ const renderOptions = {
368
+ ...streamOptions,
369
+ };
370
+ if (options?.width === undefined) {
371
+ const columns = plotStream.columns ?? process.stdout.columns ?? 80;
372
+ renderOptions.width = Math.max(10, columns - 8);
373
+ }
374
+ const coordinates = (seriesCount === 1 ? seriesPoints[0] : seriesPoints);
375
+ const output = (0, simple_ascii_chart_1.default)(coordinates, renderOptions);
376
+ plotStream.write(`${clearScreen}${output}`);
377
+ lastRenderAt = Date.now();
378
+ };
379
+ const scheduleRender = (force = false) => {
380
+ if (force) {
381
+ if (renderTimer) {
382
+ clearTimeout(renderTimer);
383
+ renderTimer = undefined;
384
+ }
385
+ hasPendingRender = false;
386
+ render();
387
+ return;
388
+ }
389
+ if (refreshMs <= 0) {
390
+ render();
391
+ return;
392
+ }
393
+ const now = Date.now();
394
+ const elapsed = now - lastRenderAt;
395
+ if (elapsed >= refreshMs) {
396
+ render();
397
+ return;
398
+ }
399
+ hasPendingRender = true;
400
+ if (!renderTimer) {
401
+ renderTimer = setTimeout(() => {
402
+ renderTimer = undefined;
403
+ if (hasPendingRender) {
404
+ hasPendingRender = false;
405
+ render();
406
+ }
407
+ }, refreshMs - elapsed);
408
+ }
409
+ };
410
+ const addSample = (sample) => {
411
+ if (streamStartX === undefined) {
412
+ streamStartX = sample.x;
413
+ }
414
+ let values = sample.values;
415
+ if (rateEnabled) {
416
+ if (!previousRateSample) {
417
+ previousRateSample = {
418
+ sampleMs: sample.sampleMs,
419
+ values: sample.values,
420
+ };
421
+ return;
422
+ }
423
+ const deltaSeconds = (sample.sampleMs - previousRateSample.sampleMs) / 1000;
424
+ const previousValues = previousRateSample.values;
425
+ previousRateSample = {
426
+ sampleMs: sample.sampleMs,
427
+ values: sample.values,
428
+ };
429
+ if (deltaSeconds <= 0) {
430
+ return;
431
+ }
432
+ values = sample.values.map((value, index) => (value - previousValues[index]) / deltaSeconds);
433
+ }
434
+ for (let index = 0; index < seriesCount; index += 1) {
435
+ seriesPoints[index].push([sample.x, values[index]]);
436
+ if (seriesPoints[index].length > window) {
437
+ seriesPoints[index].splice(0, seriesPoints[index].length - window);
438
+ }
439
+ }
440
+ scheduleRender(false);
441
+ };
442
+ const finish = () => {
443
+ if (isFinished) {
444
+ return;
445
+ }
446
+ isFinished = true;
447
+ if (renderTimer) {
448
+ clearTimeout(renderTimer);
449
+ renderTimer = undefined;
450
+ }
451
+ process.off('SIGWINCH', onResize);
452
+ process.off('SIGINT', onSigInt);
453
+ if (hasPendingRender || seriesPoints[0].length > 0) {
454
+ scheduleRender(true);
455
+ }
456
+ plotStream.write('\n');
457
+ process.exit(0);
458
+ };
459
+ const onResize = () => {
460
+ scheduleRender(true);
461
+ };
462
+ const stream = readline.createInterface({
463
+ input: process.stdin,
464
+ crlfDelay: Infinity,
465
+ terminal: false,
466
+ });
467
+ const onSigInt = () => {
468
+ stream.close();
469
+ finish();
470
+ };
471
+ process.on('SIGWINCH', onResize);
472
+ process.on('SIGINT', onSigInt);
473
+ stream.on('line', (line) => {
474
+ if (passthrough) {
475
+ process.stdout.write(`${line}\n`);
476
+ }
477
+ const parsed = parseStreamSample({
478
+ line,
479
+ lastAutoX: nextAutoX,
480
+ seriesCount,
481
+ });
482
+ nextAutoX = parsed.nextAutoX;
483
+ if ('x' in parsed) {
484
+ addSample(parsed);
485
+ }
486
+ });
487
+ stream.on('close', finish);
488
+ };
489
+ const reportError = (error, verbose) => {
490
+ const primaryMessage = error instanceof CliError
491
+ ? error.message
492
+ : error instanceof Error
493
+ ? error.message
494
+ : `Unexpected error: ${String(error)}`;
495
+ process.stderr.write(`${primaryMessage}\n`);
496
+ if (verbose) {
497
+ if (error instanceof CliError && error.cause instanceof Error && error.cause.stack) {
498
+ process.stderr.write(`${error.cause.stack}\n`);
499
+ }
500
+ else if (error instanceof Error && error.stack) {
501
+ process.stderr.write(`${error.stack}\n`);
502
+ }
503
+ }
504
+ process.exit(1);
505
+ };
506
+ const run = async (argumentsInput) => {
507
+ if (argumentsInput.passthrough && !argumentsInput.stream) {
508
+ fail('--passthrough is only supported with --stream mode');
509
+ }
510
+ if (argumentsInput.rate && !argumentsInput.stream) {
511
+ fail('--rate is only supported with --stream mode');
512
+ }
513
+ const warnings = [];
514
+ const parsedOptions = (0, options_1.preparePlotOptions)({
515
+ ...argumentsInput,
516
+ mode: argumentsInput.mode,
517
+ onWarning: (message) => warnings.push(message),
518
+ });
519
+ warnings.forEach((message) => {
520
+ process.stderr.write(`Warning: ${message}\n`);
521
+ });
522
+ const plotOutput = (argumentsInput.plotOutput ??
523
+ (argumentsInput.passthrough ? 'stderr' : 'stdout'));
524
+ if (argumentsInput.stream) {
525
+ executeStream({
526
+ options: parsedOptions,
527
+ plotOutput,
528
+ refreshMs: argumentsInput.refreshMs ?? 200,
529
+ seriesCount: argumentsInput.series ?? 1,
530
+ rateEnabled: argumentsInput.rate ?? false,
531
+ window: argumentsInput.window ?? 60,
532
+ passthrough: argumentsInput.passthrough ?? false,
533
+ });
534
+ return;
535
+ }
536
+ const input = await getStaticInput(argumentsInput);
537
+ executeStatic({
538
+ input,
539
+ options: parsedOptions,
540
+ plotOutput,
541
+ });
542
+ };
543
+ const { argv } = yargs
57
544
  .option('input', {
58
545
  alias: 'i',
59
546
  type: 'string',
60
- demandOption: true,
61
- description: 'Data to be plotted (in JSON format)',
547
+ description: 'Inline data payload. Usually JSON coordinates (for example [[1,2],[2,3]])',
548
+ })
549
+ .option('inputFile', {
550
+ alias: ['input-file'],
551
+ type: 'string',
552
+ description: 'Read input data from a file path',
553
+ })
554
+ .option('format', {
555
+ choices: ['json', 'csv', 'tsv', 'space'],
556
+ description: 'Input format for --input-file or stdin. Auto-detected when omitted',
557
+ })
558
+ .option('delimiter', {
559
+ type: 'string',
560
+ description: 'Custom delimiter for delimited formats',
561
+ })
562
+ .option('header', {
563
+ type: 'boolean',
564
+ default: false,
565
+ description: 'Treat the first row in delimited input as a header row',
566
+ })
567
+ .option('xCol', {
568
+ alias: ['x-col'],
569
+ type: 'string',
570
+ description: 'X column selector for delimited input (1-based index or header name)',
571
+ })
572
+ .option('yCol', {
573
+ alias: ['y-col'],
574
+ type: 'string',
575
+ description: 'Y column selector for delimited input (1-based index or header name)',
576
+ })
577
+ .option('stream', {
578
+ type: 'boolean',
579
+ default: false,
580
+ description: 'Enable streaming mode and read newline-delimited samples from stdin',
581
+ })
582
+ .option('window', {
583
+ type: 'number',
584
+ default: 60,
585
+ description: 'Maximum number of recent samples to keep in stream mode',
586
+ })
587
+ .option('refreshMs', {
588
+ alias: ['refresh-ms'],
589
+ type: 'number',
590
+ default: 200,
591
+ description: 'Minimum render interval in milliseconds for stream mode',
592
+ })
593
+ .option('rate', {
594
+ type: 'boolean',
595
+ default: false,
596
+ description: 'Treat stream values as counters and plot per-second rates',
597
+ })
598
+ .option('series', {
599
+ choices: [1, 2],
600
+ default: 1,
601
+ description: 'Number of stream series to parse per line',
602
+ })
603
+ .option('passthrough', {
604
+ type: 'boolean',
605
+ default: false,
606
+ description: 'Forward incoming stream lines to stdout while plotting',
607
+ })
608
+ .option('plotOutput', {
609
+ alias: ['plot-output'],
610
+ choices: ['stdout', 'stderr'],
611
+ description: 'Output stream used for chart rendering',
62
612
  })
63
613
  .option('options', {
64
614
  alias: 'o',
65
615
  type: 'string',
66
- description: 'Plot settings (in JSON format)',
616
+ description: 'Plot settings (JSON object)',
67
617
  })
68
618
  .option('height', {
69
619
  alias: 'h',
@@ -75,8 +625,13 @@ var argv = yargs
75
625
  description: 'Hide the x-axis if set to true',
76
626
  })
77
627
  .option('mode', {
78
- type: 'string',
79
- description: 'Sets mode for the plot (e.g., "line", "bar", "horizontalBar", "point")',
628
+ choices: options_1.GRAPH_MODES,
629
+ description: 'Plot mode',
630
+ })
631
+ .option('debugMode', {
632
+ alias: ['debug-mode'],
633
+ type: 'boolean',
634
+ description: 'Enable debug mode in the chart engine',
80
635
  })
81
636
  .option('hideYAxis', {
82
637
  type: 'boolean',
@@ -107,7 +662,7 @@ var argv = yargs
107
662
  .option('color', {
108
663
  alias: 'c',
109
664
  type: 'array',
110
- description: 'Array of colors for plot elements',
665
+ description: 'Array of ANSI colors for plot elements',
111
666
  })
112
667
  .option('axisCenter', {
113
668
  type: 'array',
@@ -123,11 +678,11 @@ var argv = yargs
123
678
  })
124
679
  .option('thresholds', {
125
680
  type: 'array',
126
- description: 'Array of threshold lines with optional color',
681
+ description: 'Threshold markers: JSON object/array string (for example \'{"y":2}\' or \'[{"y":2}]\') or tokenized JSON objects',
127
682
  })
128
683
  .option('points', {
129
684
  type: 'array',
130
- description: 'Array of points with optional color',
685
+ description: 'Point markers: JSON object/array string (for example \'{"x":1,"y":2}\' or \'[{"x":1,"y":2}]\') or tokenized JSON objects',
131
686
  })
132
687
  .option('legend', {
133
688
  type: 'string',
@@ -144,50 +699,53 @@ var argv = yargs
144
699
  .option('symbols', {
145
700
  type: 'string',
146
701
  description: 'Custom symbols for axis, chart, and background',
147
- }).argv;
148
- // Helper function to execute code with error handling
149
- var withError = function (cb) {
702
+ })
703
+ .option('verbose', {
704
+ type: 'boolean',
705
+ default: false,
706
+ description: 'Print verbose errors',
707
+ })
708
+ .check((argumentsInput) => {
709
+ if (argumentsInput.window !== undefined &&
710
+ (typeof argumentsInput.window !== 'number' ||
711
+ !Number.isFinite(argumentsInput.window) ||
712
+ argumentsInput.window <= 0)) {
713
+ throw new Error('window must be a positive number');
714
+ }
715
+ if (argumentsInput.refreshMs !== undefined &&
716
+ (typeof argumentsInput.refreshMs !== 'number' ||
717
+ !Number.isFinite(argumentsInput.refreshMs) ||
718
+ argumentsInput.refreshMs < 0)) {
719
+ throw new Error('refreshMs must be a non-negative number');
720
+ }
721
+ if (argumentsInput.width !== undefined &&
722
+ (typeof argumentsInput.width !== 'number' ||
723
+ !Number.isFinite(argumentsInput.width) ||
724
+ argumentsInput.width <= 0)) {
725
+ throw new Error('width must be a positive number');
726
+ }
727
+ if (argumentsInput.height !== undefined &&
728
+ (typeof argumentsInput.height !== 'number' ||
729
+ !Number.isFinite(argumentsInput.height) ||
730
+ argumentsInput.height <= 0)) {
731
+ throw new Error('height must be a positive number');
732
+ }
733
+ return true;
734
+ });
735
+ const runMain = async (parsedArguments) => {
736
+ const args = parsedArguments;
150
737
  try {
151
- cb();
738
+ await run(args);
152
739
  }
153
740
  catch (error) {
154
- // Display an error message and exit the process with a failure code
155
- process.stderr.write('Oops! Something went wrong!\n');
156
- process.exit(1);
157
- }
158
- };
159
- // Main function to execute the plot based on input and options
160
- var execute = function (_a) {
161
- var input = _a.input, options = _a.options;
162
- withError(function () {
163
- // Generate the ASCII plot based on provided input and settings
164
- var output = (0, simple_ascii_chart_1.default)(input, options);
165
- // Output the result to the console
166
- process.stdout.write(output);
167
- process.exit(0); // Exit successfully after outputting the plot
168
- });
169
- };
170
- var prepareParams = function (_a) {
171
- var input = _a.input, options = _a.options, width = _a.width, height = _a.height, hideYAxis = _a.hideYAxis, hideXAxis = _a.hideXAxis, fillArea = _a.fillArea, title = _a.title, xLabel = _a.xLabel, yLabel = _a.yLabel, color = _a.color, axisCenter = _a.axisCenter, yRange = _a.yRange, showTickLabel = _a.showTickLabel, thresholds = _a.thresholds, points = _a.points, legend = _a.legend, formatter = _a.formatter, lineFormatter = _a.lineFormatter, symbols = _a.symbols, mode = _a.mode;
172
- var currentOptions = options ? JSON.parse(options) : {};
173
- return {
174
- input: JSON.parse(input),
175
- options: __assign(__assign({}, currentOptions), { width: width, height: height, hideYAxis: hideYAxis, hideXAxis: hideXAxis, title: title, xLabel: xLabel, yLabel: yLabel, fillArea: fillArea, mode: mode, color: color ? (0, validators_1.validateColors)(color) : undefined, axisCenter: (0, validators_1.validateAxisCenter)(axisCenter), yRange: (0, validators_1.validateYRange)(yRange), // Validate and format yRange
176
- showTickLabel: showTickLabel, thresholds: (0, validators_1.validateThresholds)(thresholds), points: (0, validators_1.validatePoints)(points), legend: (0, validators_1.validateLegend)(legend), formatter: (0, validators_1.validateFormatter)(formatter), lineFormatter: (0, validators_1.validateLineFormatter)(lineFormatter), symbols: (0, validators_1.validateSymbols)(symbols) }),
177
- };
741
+ reportError(error, args.verbose ?? false);
742
+ }
178
743
  };
179
- // Check if argv is a Promise to handle async parsing in yargs
180
744
  if (argv instanceof Promise) {
181
- // If async, wait for arguments and execute the plotting
182
- argv.then(function (parameters) {
183
- withError(function () {
184
- execute(prepareParams(parameters));
185
- });
745
+ argv.then((parsedArguments) => {
746
+ runMain(parsedArguments);
186
747
  });
187
748
  }
188
749
  else {
189
- // Synchronously prepare parameters and execute the plot
190
- withError(function () {
191
- execute(prepareParams(argv));
192
- });
750
+ runMain(argv);
193
751
  }