document-cli 1.2.11 → 1.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.
- package/dist/cli.js +183 -4
- package/dist/index.cjs +181 -2
- package/dist/index.js +182 -3
- package/dist/{tui-DD4bQp39.js → tui-C8ITn1zQ.js} +154 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { a as odbFormSummary, c as writeOutput, d as inferFormatFromExtension, f as isDocumentFormat, i as formatOdbReportLines, l as loadProvidedFonts, n as describeOdbReport, o as readInput, r as formatOdbFormLines, s as resolveDefaultOutputPath, t as describeOdbForm } from "./odb-structure-CX_0zL8_.js";
|
|
3
3
|
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
4
4
|
import { writeFile } from "node:fs/promises";
|
|
5
|
-
import { OdbNoEmbeddedDataSourceError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, createLocalDocumentConverter, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, odbToCsv, odbToXlsx, odmToPdf, readOdbForms, readOdbReports, readOdbTables, readPdf, writePdf } from "documents.js";
|
|
5
|
+
import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, convertWordprocessingToLayout, createFontMeasurer, createFontRegistry, createLocalDocumentConverter, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, evaluateSelect, hsqldbCellDisplayText, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, writePdf } from "documents.js";
|
|
6
6
|
import { basename, dirname, extname, join } from "node:path";
|
|
7
7
|
import { decodePackage, encodePackage as encodePackage$1 } from "odf.js";
|
|
8
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -114,7 +114,8 @@ function pdfDiagnosticToDiagnostic(diagnostic) {
|
|
|
114
114
|
function mapErrorToExit(error, abortReason) {
|
|
115
115
|
if (abortReason === "interrupt") return 130;
|
|
116
116
|
if (abortReason === "timeout") return 124;
|
|
117
|
-
if (error instanceof OdmUnresolvedSectionError || error instanceof OdbTableNotSpecifiedError || error instanceof OdbTableNotFoundError || error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError) return 3;
|
|
117
|
+
if (error instanceof OdmUnresolvedSectionError || error instanceof OdbTableNotSpecifiedError || error instanceof OdbTableNotFoundError || error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError || error instanceof OdbReportNotSpecifiedError) return 3;
|
|
118
|
+
if (error instanceof HsqldbSqlUnsupportedError || error instanceof HsqldbSqlParseError || error instanceof HsqldbSqlEvaluationError) return 1;
|
|
118
119
|
if (error instanceof PdfEncryptedError || error instanceof PdfParseError) return 1;
|
|
119
120
|
return 1;
|
|
120
121
|
}
|
|
@@ -373,6 +374,27 @@ function registerFromPackageCommand(program) {
|
|
|
373
374
|
});
|
|
374
375
|
}
|
|
375
376
|
//#endregion
|
|
377
|
+
//#region src/sql-result-format.ts
|
|
378
|
+
const COLUMN_GAP = " ";
|
|
379
|
+
function columnWidths(columns, cells) {
|
|
380
|
+
return columns.map((column, index) => {
|
|
381
|
+
const cellWidths = cells.map((row) => row[index]?.length ?? 0);
|
|
382
|
+
return Math.max(column.length, ...cellWidths);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
function formatRow(values, widths) {
|
|
386
|
+
return values.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(COLUMN_GAP).trimEnd();
|
|
387
|
+
}
|
|
388
|
+
function formatSqlResultSetTable(result) {
|
|
389
|
+
const { columns, rows } = result;
|
|
390
|
+
const cells = rows.map((row) => row.map((value) => hsqldbCellDisplayText(value)));
|
|
391
|
+
const widths = columnWidths(columns, cells);
|
|
392
|
+
const lines = [formatRow(columns, widths), formatRow(widths.map((width) => "-".repeat(width)), widths)];
|
|
393
|
+
for (const row of cells) lines.push(formatRow(row, widths));
|
|
394
|
+
lines.push(`${rows.length} row${rows.length === 1 ? "" : "s"}`);
|
|
395
|
+
return lines;
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
376
398
|
//#region src/commands/odb.ts
|
|
377
399
|
function reportOdbError(command, error, verbose, abortReason) {
|
|
378
400
|
if (error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError) {
|
|
@@ -389,6 +411,13 @@ function reportOdbCsvError(command, error, verbose, abortReason) {
|
|
|
389
411
|
}
|
|
390
412
|
return reportOdbError(command, error, verbose, abortReason);
|
|
391
413
|
}
|
|
414
|
+
function reportOdbReportError(command, error, verbose, abortReason) {
|
|
415
|
+
if (error instanceof OdbReportNotSpecifiedError) {
|
|
416
|
+
process.stderr.write(`[${command}] ${error.message}\nrun 'odb-reports' first to see the available reports\n`);
|
|
417
|
+
return mapErrorToExit(error, abortReason);
|
|
418
|
+
}
|
|
419
|
+
return reportOdbError(command, error, verbose, abortReason);
|
|
420
|
+
}
|
|
392
421
|
function resolveDefaultCsvOutputPath(inputPath) {
|
|
393
422
|
const directory = dirname(inputPath);
|
|
394
423
|
const stem = basename(inputPath, extname(inputPath));
|
|
@@ -474,6 +503,48 @@ async function runOdbTables(input, options) {
|
|
|
474
503
|
return reportOdbError(command, error, false, getAbortReason());
|
|
475
504
|
}
|
|
476
505
|
}
|
|
506
|
+
function resolveQuerySql(pkg, options) {
|
|
507
|
+
if (options.sql !== void 0) return { sql: options.sql };
|
|
508
|
+
const queryName = options.query;
|
|
509
|
+
if (queryName === void 0) return { errorMessage: "pass --sql <text> or --query <savedName>" };
|
|
510
|
+
const inventory = readOdbInventory(pkg);
|
|
511
|
+
const saved = inventory.queries.find((candidate) => candidate.name === queryName);
|
|
512
|
+
if (saved === void 0) {
|
|
513
|
+
const available = inventory.queries.map((candidate) => candidate.name);
|
|
514
|
+
return { errorMessage: `this .odb declares no saved query named '${queryName}'${available.length === 0 ? "" : ` -- available: ${available.join(", ")}`}` };
|
|
515
|
+
}
|
|
516
|
+
return { sql: saved.command };
|
|
517
|
+
}
|
|
518
|
+
async function runOdbQuery(input, options) {
|
|
519
|
+
const command = "odb-query";
|
|
520
|
+
if (options.sql !== void 0 && options.query !== void 0) {
|
|
521
|
+
process.stderr.write(`[${command}] pass --sql or --query, not both\n`);
|
|
522
|
+
return 2;
|
|
523
|
+
}
|
|
524
|
+
if (options.sql === void 0 && options.query === void 0) {
|
|
525
|
+
process.stderr.write(`[${command}] pass --sql <text> or --query <savedName>\n`);
|
|
526
|
+
return 2;
|
|
527
|
+
}
|
|
528
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
529
|
+
try {
|
|
530
|
+
const inputBytes = await readInput(input, { signal });
|
|
531
|
+
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
532
|
+
const resolved = resolveQuerySql(pkg, options);
|
|
533
|
+
if ("errorMessage" in resolved) {
|
|
534
|
+
process.stderr.write(`[${command}] ${resolved.errorMessage}\n`);
|
|
535
|
+
return 2;
|
|
536
|
+
}
|
|
537
|
+
const result = evaluateSelect(parseSelect(resolved.sql), readOdbTables(pkg));
|
|
538
|
+
if (options.json) {
|
|
539
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
540
|
+
return 0;
|
|
541
|
+
}
|
|
542
|
+
for (const line of formatSqlResultSetTable(result)) process.stdout.write(`${line}\n`);
|
|
543
|
+
return 0;
|
|
544
|
+
} catch (error) {
|
|
545
|
+
return reportOdbError(command, error, false, getAbortReason());
|
|
546
|
+
}
|
|
547
|
+
}
|
|
477
548
|
async function runOdbForms(input, options) {
|
|
478
549
|
const command = "odb-forms";
|
|
479
550
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
@@ -520,6 +591,93 @@ async function runOdbReports(input, options) {
|
|
|
520
591
|
return reportOdbError(command, error, false, getAbortReason());
|
|
521
592
|
}
|
|
522
593
|
}
|
|
594
|
+
const ODB_REPORT_TARGET_FORMATS = {
|
|
595
|
+
docx: true,
|
|
596
|
+
odt: true,
|
|
597
|
+
pdf: true
|
|
598
|
+
};
|
|
599
|
+
function isOdbReportTargetFormat(format) {
|
|
600
|
+
return format in ODB_REPORT_TARGET_FORMATS;
|
|
601
|
+
}
|
|
602
|
+
function renderOdbReportBytes(content, target, options) {
|
|
603
|
+
if (target === "docx") return encodePackage(buildDocxPackage(content));
|
|
604
|
+
if (target === "odt") return encodePackage$1(buildOdtPackage(content));
|
|
605
|
+
if (content.kind !== "wordprocessing") throw new Error("readOdbReportContent returned a non-wordprocessing ContentDocument");
|
|
606
|
+
const fonts = createFontRegistry({
|
|
607
|
+
fonts: options.fonts,
|
|
608
|
+
onSubstitution: (substitution) => {
|
|
609
|
+
if (options.reportFontSubstitution !== void 0) {
|
|
610
|
+
options.reportFontSubstitution(substitution);
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
options.onDiagnosticCounted();
|
|
614
|
+
options.reporter.report(fontSubstitutionToDiagnostic(substitution));
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
618
|
+
return writePdf(layout, {
|
|
619
|
+
signal: options.signal,
|
|
620
|
+
onSubstitution: (substitution, context) => {
|
|
621
|
+
options.onDiagnosticCounted();
|
|
622
|
+
options.reporter.report(substitutionToDiagnostic(substitution, context.pageIndex));
|
|
623
|
+
},
|
|
624
|
+
formulas,
|
|
625
|
+
fonts
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
async function runOdbRenderReport(input, output, options) {
|
|
629
|
+
const command = "odb-render-report";
|
|
630
|
+
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
631
|
+
process.stderr.write(`[${command}] conflicting output destinations: positional '${output}' and --out '${options.out}'\n`);
|
|
632
|
+
return 2;
|
|
633
|
+
}
|
|
634
|
+
const target = resolveTargetFormat(output, options.out, options.to);
|
|
635
|
+
if ("errorMessage" in target) {
|
|
636
|
+
process.stderr.write(`[${command}] ${target.errorMessage}\n`);
|
|
637
|
+
return 2;
|
|
638
|
+
}
|
|
639
|
+
if (!isOdbReportTargetFormat(target.format)) {
|
|
640
|
+
process.stderr.write(`[${command}] '${target.format}' is not a supported report render target; expected one of docx, odt, pdf\n`);
|
|
641
|
+
return 2;
|
|
642
|
+
}
|
|
643
|
+
const targetFormat = target.format;
|
|
644
|
+
const resolvedOutput = output ?? options.out ?? (input === "-" ? "-" : resolveDefaultOutputPath(input, targetFormat));
|
|
645
|
+
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
646
|
+
const reporter = createDiagnosticReporter({
|
|
647
|
+
json: options.json,
|
|
648
|
+
quiet: options.quiet,
|
|
649
|
+
command
|
|
650
|
+
});
|
|
651
|
+
let diagnosticCount = 0;
|
|
652
|
+
const reportFontSubstitution = options.reportFontSubstitutions === true ? createFontSubstitutionReporter({
|
|
653
|
+
json: options.json,
|
|
654
|
+
quiet: options.quiet,
|
|
655
|
+
command
|
|
656
|
+
}) : void 0;
|
|
657
|
+
try {
|
|
658
|
+
const inputBytes = await readInput(input, { signal });
|
|
659
|
+
const fonts = await loadProvidedFonts(options.fontFile ?? [], { signal });
|
|
660
|
+
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
661
|
+
const bytes = renderOdbReportBytes(readOdbReportContent(pkg, { report: options.report }), targetFormat, {
|
|
662
|
+
fonts,
|
|
663
|
+
signal,
|
|
664
|
+
reporter,
|
|
665
|
+
reportFontSubstitution,
|
|
666
|
+
onDiagnosticCounted: () => {
|
|
667
|
+
diagnosticCount += 1;
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
await writeOutput(resolvedOutput, bytes);
|
|
671
|
+
reporter.summarize({
|
|
672
|
+
output: resolvedOutput,
|
|
673
|
+
bytes: bytes.byteLength,
|
|
674
|
+
diagnosticCount
|
|
675
|
+
});
|
|
676
|
+
return 0;
|
|
677
|
+
} catch (error) {
|
|
678
|
+
return reportOdbReportError(command, error, options.verbose, getAbortReason());
|
|
679
|
+
}
|
|
680
|
+
}
|
|
523
681
|
function registerOdbToXlsxCommand(program) {
|
|
524
682
|
const command = program.command("odb-to-xlsx <input> [output]").description("extract every table an embedded .odb database declares into one xlsx workbook, one sheet per table");
|
|
525
683
|
addOutOption(command);
|
|
@@ -558,12 +716,33 @@ function registerOdbReportsCommand(program) {
|
|
|
558
716
|
process.exitCode = await runOdbReports(input, options);
|
|
559
717
|
});
|
|
560
718
|
}
|
|
719
|
+
function registerOdbQueryCommand(program) {
|
|
720
|
+
program.command("odb-query <input>").description("run a bounded SELECT over an embedded .odb database's own extracted tables, given directly or by naming one of its saved queries").option("--sql <text>", "the SELECT statement to run -- mutually exclusive with --query").option("--query <savedName>", "the name of one of the .odb's own saved queries to run -- mutually exclusive with --sql").option("--json", "emit the result set as JSON ({ columns, rows }) instead of a plain-text table", false).action(async (input, options) => {
|
|
721
|
+
process.exitCode = await runOdbQuery(input, options);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
function registerOdbRenderReportCommand(program) {
|
|
725
|
+
const command = program.command("odb-render-report <input> [output]").description("render one of an .odb's own reports -- its query resolved, its rpt: formulas evaluated, its bands laid out -- to docx, odt, or pdf");
|
|
726
|
+
addOutOption(command);
|
|
727
|
+
addTimeoutOption(command);
|
|
728
|
+
addJsonOption(command);
|
|
729
|
+
addQuietOption(command);
|
|
730
|
+
addVerboseOption(command);
|
|
731
|
+
addFontOptions(command);
|
|
732
|
+
command.option("--report <name>", "the report to render -- required only when the .odb declares more than one report");
|
|
733
|
+
command.option("--to <format>", "target format when it cannot be inferred from the output path (docx, odt, pdf)");
|
|
734
|
+
command.action(async (input, output, options) => {
|
|
735
|
+
process.exitCode = await runOdbRenderReport(input, output, options);
|
|
736
|
+
});
|
|
737
|
+
}
|
|
561
738
|
function registerOdbCommands(program) {
|
|
562
739
|
registerOdbToXlsxCommand(program);
|
|
563
740
|
registerOdbToCsvCommand(program);
|
|
564
741
|
registerOdbTablesCommand(program);
|
|
565
742
|
registerOdbFormsCommand(program);
|
|
566
743
|
registerOdbReportsCommand(program);
|
|
744
|
+
registerOdbQueryCommand(program);
|
|
745
|
+
registerOdbRenderReportCommand(program);
|
|
567
746
|
}
|
|
568
747
|
//#endregion
|
|
569
748
|
//#region src/commands/odm.ts
|
|
@@ -744,7 +923,7 @@ function registerPdfInspectCommand(program) {
|
|
|
744
923
|
}
|
|
745
924
|
//#endregion
|
|
746
925
|
//#region package.json
|
|
747
|
-
var version = "1.
|
|
926
|
+
var version = "1.3.0";
|
|
748
927
|
//#endregion
|
|
749
928
|
//#region src/program.ts
|
|
750
929
|
function createProgram() {
|
|
@@ -767,7 +946,7 @@ function createProgram() {
|
|
|
767
946
|
//#region src/cli.ts
|
|
768
947
|
async function launchTui(startPath, signal) {
|
|
769
948
|
try {
|
|
770
|
-
const { runTui } = await import("./tui-
|
|
949
|
+
const { runTui } = await import("./tui-C8ITn1zQ.js");
|
|
771
950
|
await runTui({
|
|
772
951
|
startPath,
|
|
773
952
|
signal
|
package/dist/index.cjs
CHANGED
|
@@ -162,7 +162,8 @@ const EXIT_INTERRUPTED = 130;
|
|
|
162
162
|
function mapErrorToExit(error, abortReason) {
|
|
163
163
|
if (abortReason === "interrupt") return 130;
|
|
164
164
|
if (abortReason === "timeout") return 124;
|
|
165
|
-
if (error instanceof documents_js.OdmUnresolvedSectionError || error instanceof documents_js.OdbTableNotSpecifiedError || error instanceof documents_js.OdbTableNotFoundError || error instanceof documents_js.OdbNoEmbeddedDataSourceError || error instanceof documents_js.OdbUnsupportedFormatError) return 3;
|
|
165
|
+
if (error instanceof documents_js.OdmUnresolvedSectionError || error instanceof documents_js.OdbTableNotSpecifiedError || error instanceof documents_js.OdbTableNotFoundError || error instanceof documents_js.OdbNoEmbeddedDataSourceError || error instanceof documents_js.OdbUnsupportedFormatError || error instanceof documents_js.OdbReportNotSpecifiedError) return 3;
|
|
166
|
+
if (error instanceof documents_js.HsqldbSqlUnsupportedError || error instanceof documents_js.HsqldbSqlParseError || error instanceof documents_js.HsqldbSqlEvaluationError) return 1;
|
|
166
167
|
if (error instanceof documents_js.PdfEncryptedError || error instanceof documents_js.PdfParseError) return 1;
|
|
167
168
|
return 1;
|
|
168
169
|
}
|
|
@@ -758,6 +759,27 @@ function formatOdbReportLines(report) {
|
|
|
758
759
|
return lines;
|
|
759
760
|
}
|
|
760
761
|
//#endregion
|
|
762
|
+
//#region src/sql-result-format.ts
|
|
763
|
+
const COLUMN_GAP = " ";
|
|
764
|
+
function columnWidths(columns, cells) {
|
|
765
|
+
return columns.map((column, index) => {
|
|
766
|
+
const cellWidths = cells.map((row) => row[index]?.length ?? 0);
|
|
767
|
+
return Math.max(column.length, ...cellWidths);
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
function formatRow(values, widths) {
|
|
771
|
+
return values.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(COLUMN_GAP).trimEnd();
|
|
772
|
+
}
|
|
773
|
+
function formatSqlResultSetTable(result) {
|
|
774
|
+
const { columns, rows } = result;
|
|
775
|
+
const cells = rows.map((row) => row.map((value) => (0, documents_js.hsqldbCellDisplayText)(value)));
|
|
776
|
+
const widths = columnWidths(columns, cells);
|
|
777
|
+
const lines = [formatRow(columns, widths), formatRow(widths.map((width) => "-".repeat(width)), widths)];
|
|
778
|
+
for (const row of cells) lines.push(formatRow(row, widths));
|
|
779
|
+
lines.push(`${rows.length} row${rows.length === 1 ? "" : "s"}`);
|
|
780
|
+
return lines;
|
|
781
|
+
}
|
|
782
|
+
//#endregion
|
|
761
783
|
//#region src/commands/odb.ts
|
|
762
784
|
function reportOdbError(command, error, verbose, abortReason) {
|
|
763
785
|
if (error instanceof documents_js.OdbNoEmbeddedDataSourceError || error instanceof documents_js.OdbUnsupportedFormatError) {
|
|
@@ -774,6 +796,13 @@ function reportOdbCsvError(command, error, verbose, abortReason) {
|
|
|
774
796
|
}
|
|
775
797
|
return reportOdbError(command, error, verbose, abortReason);
|
|
776
798
|
}
|
|
799
|
+
function reportOdbReportError(command, error, verbose, abortReason) {
|
|
800
|
+
if (error instanceof documents_js.OdbReportNotSpecifiedError) {
|
|
801
|
+
process.stderr.write(`[${command}] ${error.message}\nrun 'odb-reports' first to see the available reports\n`);
|
|
802
|
+
return mapErrorToExit(error, abortReason);
|
|
803
|
+
}
|
|
804
|
+
return reportOdbError(command, error, verbose, abortReason);
|
|
805
|
+
}
|
|
777
806
|
function resolveDefaultCsvOutputPath(inputPath) {
|
|
778
807
|
const directory = (0, node_path.dirname)(inputPath);
|
|
779
808
|
const stem = (0, node_path.basename)(inputPath, (0, node_path.extname)(inputPath));
|
|
@@ -859,6 +888,48 @@ async function runOdbTables(input, options) {
|
|
|
859
888
|
return reportOdbError(command, error, false, getAbortReason());
|
|
860
889
|
}
|
|
861
890
|
}
|
|
891
|
+
function resolveQuerySql(pkg, options) {
|
|
892
|
+
if (options.sql !== void 0) return { sql: options.sql };
|
|
893
|
+
const queryName = options.query;
|
|
894
|
+
if (queryName === void 0) return { errorMessage: "pass --sql <text> or --query <savedName>" };
|
|
895
|
+
const inventory = (0, documents_js.readOdbInventory)(pkg);
|
|
896
|
+
const saved = inventory.queries.find((candidate) => candidate.name === queryName);
|
|
897
|
+
if (saved === void 0) {
|
|
898
|
+
const available = inventory.queries.map((candidate) => candidate.name);
|
|
899
|
+
return { errorMessage: `this .odb declares no saved query named '${queryName}'${available.length === 0 ? "" : ` -- available: ${available.join(", ")}`}` };
|
|
900
|
+
}
|
|
901
|
+
return { sql: saved.command };
|
|
902
|
+
}
|
|
903
|
+
async function runOdbQuery(input, options) {
|
|
904
|
+
const command = "odb-query";
|
|
905
|
+
if (options.sql !== void 0 && options.query !== void 0) {
|
|
906
|
+
process.stderr.write(`[${command}] pass --sql or --query, not both\n`);
|
|
907
|
+
return 2;
|
|
908
|
+
}
|
|
909
|
+
if (options.sql === void 0 && options.query === void 0) {
|
|
910
|
+
process.stderr.write(`[${command}] pass --sql <text> or --query <savedName>\n`);
|
|
911
|
+
return 2;
|
|
912
|
+
}
|
|
913
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
914
|
+
try {
|
|
915
|
+
const inputBytes = await readInput(input, { signal });
|
|
916
|
+
const pkg = (0, odf_js.decodePackage)(new Uint8Array(inputBytes));
|
|
917
|
+
const resolved = resolveQuerySql(pkg, options);
|
|
918
|
+
if ("errorMessage" in resolved) {
|
|
919
|
+
process.stderr.write(`[${command}] ${resolved.errorMessage}\n`);
|
|
920
|
+
return 2;
|
|
921
|
+
}
|
|
922
|
+
const result = (0, documents_js.evaluateSelect)((0, documents_js.parseSelect)(resolved.sql), (0, documents_js.readOdbTables)(pkg));
|
|
923
|
+
if (options.json) {
|
|
924
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
925
|
+
return 0;
|
|
926
|
+
}
|
|
927
|
+
for (const line of formatSqlResultSetTable(result)) process.stdout.write(`${line}\n`);
|
|
928
|
+
return 0;
|
|
929
|
+
} catch (error) {
|
|
930
|
+
return reportOdbError(command, error, false, getAbortReason());
|
|
931
|
+
}
|
|
932
|
+
}
|
|
862
933
|
async function runOdbForms(input, options) {
|
|
863
934
|
const command = "odb-forms";
|
|
864
935
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
@@ -905,6 +976,93 @@ async function runOdbReports(input, options) {
|
|
|
905
976
|
return reportOdbError(command, error, false, getAbortReason());
|
|
906
977
|
}
|
|
907
978
|
}
|
|
979
|
+
const ODB_REPORT_TARGET_FORMATS = {
|
|
980
|
+
docx: true,
|
|
981
|
+
odt: true,
|
|
982
|
+
pdf: true
|
|
983
|
+
};
|
|
984
|
+
function isOdbReportTargetFormat(format) {
|
|
985
|
+
return format in ODB_REPORT_TARGET_FORMATS;
|
|
986
|
+
}
|
|
987
|
+
function renderOdbReportBytes(content, target, options) {
|
|
988
|
+
if (target === "docx") return (0, documents_js.encodePackage)((0, documents_js.buildDocxPackage)(content));
|
|
989
|
+
if (target === "odt") return (0, odf_js.encodePackage)((0, documents_js.buildOdtPackage)(content));
|
|
990
|
+
if (content.kind !== "wordprocessing") throw new Error("readOdbReportContent returned a non-wordprocessing ContentDocument");
|
|
991
|
+
const fonts = (0, documents_js.createFontRegistry)({
|
|
992
|
+
fonts: options.fonts,
|
|
993
|
+
onSubstitution: (substitution) => {
|
|
994
|
+
if (options.reportFontSubstitution !== void 0) {
|
|
995
|
+
options.reportFontSubstitution(substitution);
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
options.onDiagnosticCounted();
|
|
999
|
+
options.reporter.report(fontSubstitutionToDiagnostic(substitution));
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
const { document: layout, formulas } = (0, documents_js.convertWordprocessingToLayout)(content, { measurer: (0, documents_js.createFontMeasurer)(fonts) });
|
|
1003
|
+
return (0, documents_js.writePdf)(layout, {
|
|
1004
|
+
signal: options.signal,
|
|
1005
|
+
onSubstitution: (substitution, context) => {
|
|
1006
|
+
options.onDiagnosticCounted();
|
|
1007
|
+
options.reporter.report(substitutionToDiagnostic(substitution, context.pageIndex));
|
|
1008
|
+
},
|
|
1009
|
+
formulas,
|
|
1010
|
+
fonts
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
async function runOdbRenderReport(input, output, options) {
|
|
1014
|
+
const command = "odb-render-report";
|
|
1015
|
+
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
1016
|
+
process.stderr.write(`[${command}] conflicting output destinations: positional '${output}' and --out '${options.out}'\n`);
|
|
1017
|
+
return 2;
|
|
1018
|
+
}
|
|
1019
|
+
const target = resolveTargetFormat(output, options.out, options.to);
|
|
1020
|
+
if ("errorMessage" in target) {
|
|
1021
|
+
process.stderr.write(`[${command}] ${target.errorMessage}\n`);
|
|
1022
|
+
return 2;
|
|
1023
|
+
}
|
|
1024
|
+
if (!isOdbReportTargetFormat(target.format)) {
|
|
1025
|
+
process.stderr.write(`[${command}] '${target.format}' is not a supported report render target; expected one of docx, odt, pdf\n`);
|
|
1026
|
+
return 2;
|
|
1027
|
+
}
|
|
1028
|
+
const targetFormat = target.format;
|
|
1029
|
+
const resolvedOutput = output ?? options.out ?? (input === "-" ? "-" : resolveDefaultOutputPath(input, targetFormat));
|
|
1030
|
+
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
1031
|
+
const reporter = createDiagnosticReporter({
|
|
1032
|
+
json: options.json,
|
|
1033
|
+
quiet: options.quiet,
|
|
1034
|
+
command
|
|
1035
|
+
});
|
|
1036
|
+
let diagnosticCount = 0;
|
|
1037
|
+
const reportFontSubstitution = options.reportFontSubstitutions === true ? createFontSubstitutionReporter({
|
|
1038
|
+
json: options.json,
|
|
1039
|
+
quiet: options.quiet,
|
|
1040
|
+
command
|
|
1041
|
+
}) : void 0;
|
|
1042
|
+
try {
|
|
1043
|
+
const inputBytes = await readInput(input, { signal });
|
|
1044
|
+
const fonts = await loadProvidedFonts(options.fontFile ?? [], { signal });
|
|
1045
|
+
const pkg = (0, odf_js.decodePackage)(new Uint8Array(inputBytes));
|
|
1046
|
+
const bytes = renderOdbReportBytes((0, documents_js.readOdbReportContent)(pkg, { report: options.report }), targetFormat, {
|
|
1047
|
+
fonts,
|
|
1048
|
+
signal,
|
|
1049
|
+
reporter,
|
|
1050
|
+
reportFontSubstitution,
|
|
1051
|
+
onDiagnosticCounted: () => {
|
|
1052
|
+
diagnosticCount += 1;
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
await writeOutput(resolvedOutput, bytes);
|
|
1056
|
+
reporter.summarize({
|
|
1057
|
+
output: resolvedOutput,
|
|
1058
|
+
bytes: bytes.byteLength,
|
|
1059
|
+
diagnosticCount
|
|
1060
|
+
});
|
|
1061
|
+
return 0;
|
|
1062
|
+
} catch (error) {
|
|
1063
|
+
return reportOdbReportError(command, error, options.verbose, getAbortReason());
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
908
1066
|
function registerOdbToXlsxCommand(program) {
|
|
909
1067
|
const command = program.command("odb-to-xlsx <input> [output]").description("extract every table an embedded .odb database declares into one xlsx workbook, one sheet per table");
|
|
910
1068
|
addOutOption(command);
|
|
@@ -943,12 +1101,33 @@ function registerOdbReportsCommand(program) {
|
|
|
943
1101
|
process.exitCode = await runOdbReports(input, options);
|
|
944
1102
|
});
|
|
945
1103
|
}
|
|
1104
|
+
function registerOdbQueryCommand(program) {
|
|
1105
|
+
program.command("odb-query <input>").description("run a bounded SELECT over an embedded .odb database's own extracted tables, given directly or by naming one of its saved queries").option("--sql <text>", "the SELECT statement to run -- mutually exclusive with --query").option("--query <savedName>", "the name of one of the .odb's own saved queries to run -- mutually exclusive with --sql").option("--json", "emit the result set as JSON ({ columns, rows }) instead of a plain-text table", false).action(async (input, options) => {
|
|
1106
|
+
process.exitCode = await runOdbQuery(input, options);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
function registerOdbRenderReportCommand(program) {
|
|
1110
|
+
const command = program.command("odb-render-report <input> [output]").description("render one of an .odb's own reports -- its query resolved, its rpt: formulas evaluated, its bands laid out -- to docx, odt, or pdf");
|
|
1111
|
+
addOutOption(command);
|
|
1112
|
+
addTimeoutOption(command);
|
|
1113
|
+
addJsonOption(command);
|
|
1114
|
+
addQuietOption(command);
|
|
1115
|
+
addVerboseOption(command);
|
|
1116
|
+
addFontOptions(command);
|
|
1117
|
+
command.option("--report <name>", "the report to render -- required only when the .odb declares more than one report");
|
|
1118
|
+
command.option("--to <format>", "target format when it cannot be inferred from the output path (docx, odt, pdf)");
|
|
1119
|
+
command.action(async (input, output, options) => {
|
|
1120
|
+
process.exitCode = await runOdbRenderReport(input, output, options);
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
946
1123
|
function registerOdbCommands(program) {
|
|
947
1124
|
registerOdbToXlsxCommand(program);
|
|
948
1125
|
registerOdbToCsvCommand(program);
|
|
949
1126
|
registerOdbTablesCommand(program);
|
|
950
1127
|
registerOdbFormsCommand(program);
|
|
951
1128
|
registerOdbReportsCommand(program);
|
|
1129
|
+
registerOdbQueryCommand(program);
|
|
1130
|
+
registerOdbRenderReportCommand(program);
|
|
952
1131
|
}
|
|
953
1132
|
//#endregion
|
|
954
1133
|
//#region src/commands/odm.ts
|
|
@@ -1129,7 +1308,7 @@ function registerPdfInspectCommand(program) {
|
|
|
1129
1308
|
}
|
|
1130
1309
|
//#endregion
|
|
1131
1310
|
//#region package.json
|
|
1132
|
-
var version = "1.
|
|
1311
|
+
var version = "1.3.0";
|
|
1133
1312
|
//#endregion
|
|
1134
1313
|
//#region src/program.ts
|
|
1135
1314
|
function createProgram() {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import { OdbNoEmbeddedDataSourceError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, createLocalDocumentConverter, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, odbToCsv, odbToXlsx, odmToPdf, readOdbForms, readOdbReports, readOdbTables, readPdf, writePdf } from "documents.js";
|
|
2
|
+
import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, buildDocxPackage, buildMarkdownText, buildOdgPackage, buildOdpPackage, buildOdsPackage, buildOdtPackage, buildPptxPackage, convertWordprocessingToLayout, createFontMeasurer, createFontRegistry, createLocalDocumentConverter, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, evaluateSelect, hsqldbCellDisplayText, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, writePdf } from "documents.js";
|
|
3
3
|
import { basename, dirname, extname, join } from "node:path";
|
|
4
4
|
import { Command, InvalidArgumentError } from "commander";
|
|
5
5
|
import { decodePackage, encodePackage as encodePackage$1 } from "odf.js";
|
|
@@ -161,7 +161,8 @@ const EXIT_INTERRUPTED = 130;
|
|
|
161
161
|
function mapErrorToExit(error, abortReason) {
|
|
162
162
|
if (abortReason === "interrupt") return 130;
|
|
163
163
|
if (abortReason === "timeout") return 124;
|
|
164
|
-
if (error instanceof OdmUnresolvedSectionError || error instanceof OdbTableNotSpecifiedError || error instanceof OdbTableNotFoundError || error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError) return 3;
|
|
164
|
+
if (error instanceof OdmUnresolvedSectionError || error instanceof OdbTableNotSpecifiedError || error instanceof OdbTableNotFoundError || error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError || error instanceof OdbReportNotSpecifiedError) return 3;
|
|
165
|
+
if (error instanceof HsqldbSqlUnsupportedError || error instanceof HsqldbSqlParseError || error instanceof HsqldbSqlEvaluationError) return 1;
|
|
165
166
|
if (error instanceof PdfEncryptedError || error instanceof PdfParseError) return 1;
|
|
166
167
|
return 1;
|
|
167
168
|
}
|
|
@@ -757,6 +758,27 @@ function formatOdbReportLines(report) {
|
|
|
757
758
|
return lines;
|
|
758
759
|
}
|
|
759
760
|
//#endregion
|
|
761
|
+
//#region src/sql-result-format.ts
|
|
762
|
+
const COLUMN_GAP = " ";
|
|
763
|
+
function columnWidths(columns, cells) {
|
|
764
|
+
return columns.map((column, index) => {
|
|
765
|
+
const cellWidths = cells.map((row) => row[index]?.length ?? 0);
|
|
766
|
+
return Math.max(column.length, ...cellWidths);
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
function formatRow(values, widths) {
|
|
770
|
+
return values.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(COLUMN_GAP).trimEnd();
|
|
771
|
+
}
|
|
772
|
+
function formatSqlResultSetTable(result) {
|
|
773
|
+
const { columns, rows } = result;
|
|
774
|
+
const cells = rows.map((row) => row.map((value) => hsqldbCellDisplayText(value)));
|
|
775
|
+
const widths = columnWidths(columns, cells);
|
|
776
|
+
const lines = [formatRow(columns, widths), formatRow(widths.map((width) => "-".repeat(width)), widths)];
|
|
777
|
+
for (const row of cells) lines.push(formatRow(row, widths));
|
|
778
|
+
lines.push(`${rows.length} row${rows.length === 1 ? "" : "s"}`);
|
|
779
|
+
return lines;
|
|
780
|
+
}
|
|
781
|
+
//#endregion
|
|
760
782
|
//#region src/commands/odb.ts
|
|
761
783
|
function reportOdbError(command, error, verbose, abortReason) {
|
|
762
784
|
if (error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError) {
|
|
@@ -773,6 +795,13 @@ function reportOdbCsvError(command, error, verbose, abortReason) {
|
|
|
773
795
|
}
|
|
774
796
|
return reportOdbError(command, error, verbose, abortReason);
|
|
775
797
|
}
|
|
798
|
+
function reportOdbReportError(command, error, verbose, abortReason) {
|
|
799
|
+
if (error instanceof OdbReportNotSpecifiedError) {
|
|
800
|
+
process.stderr.write(`[${command}] ${error.message}\nrun 'odb-reports' first to see the available reports\n`);
|
|
801
|
+
return mapErrorToExit(error, abortReason);
|
|
802
|
+
}
|
|
803
|
+
return reportOdbError(command, error, verbose, abortReason);
|
|
804
|
+
}
|
|
776
805
|
function resolveDefaultCsvOutputPath(inputPath) {
|
|
777
806
|
const directory = dirname(inputPath);
|
|
778
807
|
const stem = basename(inputPath, extname(inputPath));
|
|
@@ -858,6 +887,48 @@ async function runOdbTables(input, options) {
|
|
|
858
887
|
return reportOdbError(command, error, false, getAbortReason());
|
|
859
888
|
}
|
|
860
889
|
}
|
|
890
|
+
function resolveQuerySql(pkg, options) {
|
|
891
|
+
if (options.sql !== void 0) return { sql: options.sql };
|
|
892
|
+
const queryName = options.query;
|
|
893
|
+
if (queryName === void 0) return { errorMessage: "pass --sql <text> or --query <savedName>" };
|
|
894
|
+
const inventory = readOdbInventory(pkg);
|
|
895
|
+
const saved = inventory.queries.find((candidate) => candidate.name === queryName);
|
|
896
|
+
if (saved === void 0) {
|
|
897
|
+
const available = inventory.queries.map((candidate) => candidate.name);
|
|
898
|
+
return { errorMessage: `this .odb declares no saved query named '${queryName}'${available.length === 0 ? "" : ` -- available: ${available.join(", ")}`}` };
|
|
899
|
+
}
|
|
900
|
+
return { sql: saved.command };
|
|
901
|
+
}
|
|
902
|
+
async function runOdbQuery(input, options) {
|
|
903
|
+
const command = "odb-query";
|
|
904
|
+
if (options.sql !== void 0 && options.query !== void 0) {
|
|
905
|
+
process.stderr.write(`[${command}] pass --sql or --query, not both\n`);
|
|
906
|
+
return 2;
|
|
907
|
+
}
|
|
908
|
+
if (options.sql === void 0 && options.query === void 0) {
|
|
909
|
+
process.stderr.write(`[${command}] pass --sql <text> or --query <savedName>\n`);
|
|
910
|
+
return 2;
|
|
911
|
+
}
|
|
912
|
+
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
913
|
+
try {
|
|
914
|
+
const inputBytes = await readInput(input, { signal });
|
|
915
|
+
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
916
|
+
const resolved = resolveQuerySql(pkg, options);
|
|
917
|
+
if ("errorMessage" in resolved) {
|
|
918
|
+
process.stderr.write(`[${command}] ${resolved.errorMessage}\n`);
|
|
919
|
+
return 2;
|
|
920
|
+
}
|
|
921
|
+
const result = evaluateSelect(parseSelect(resolved.sql), readOdbTables(pkg));
|
|
922
|
+
if (options.json) {
|
|
923
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
924
|
+
return 0;
|
|
925
|
+
}
|
|
926
|
+
for (const line of formatSqlResultSetTable(result)) process.stdout.write(`${line}\n`);
|
|
927
|
+
return 0;
|
|
928
|
+
} catch (error) {
|
|
929
|
+
return reportOdbError(command, error, false, getAbortReason());
|
|
930
|
+
}
|
|
931
|
+
}
|
|
861
932
|
async function runOdbForms(input, options) {
|
|
862
933
|
const command = "odb-forms";
|
|
863
934
|
const { signal, getAbortReason } = createRuntimeSignal({});
|
|
@@ -904,6 +975,93 @@ async function runOdbReports(input, options) {
|
|
|
904
975
|
return reportOdbError(command, error, false, getAbortReason());
|
|
905
976
|
}
|
|
906
977
|
}
|
|
978
|
+
const ODB_REPORT_TARGET_FORMATS = {
|
|
979
|
+
docx: true,
|
|
980
|
+
odt: true,
|
|
981
|
+
pdf: true
|
|
982
|
+
};
|
|
983
|
+
function isOdbReportTargetFormat(format) {
|
|
984
|
+
return format in ODB_REPORT_TARGET_FORMATS;
|
|
985
|
+
}
|
|
986
|
+
function renderOdbReportBytes(content, target, options) {
|
|
987
|
+
if (target === "docx") return encodePackage(buildDocxPackage(content));
|
|
988
|
+
if (target === "odt") return encodePackage$1(buildOdtPackage(content));
|
|
989
|
+
if (content.kind !== "wordprocessing") throw new Error("readOdbReportContent returned a non-wordprocessing ContentDocument");
|
|
990
|
+
const fonts = createFontRegistry({
|
|
991
|
+
fonts: options.fonts,
|
|
992
|
+
onSubstitution: (substitution) => {
|
|
993
|
+
if (options.reportFontSubstitution !== void 0) {
|
|
994
|
+
options.reportFontSubstitution(substitution);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
options.onDiagnosticCounted();
|
|
998
|
+
options.reporter.report(fontSubstitutionToDiagnostic(substitution));
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
|
|
1002
|
+
return writePdf(layout, {
|
|
1003
|
+
signal: options.signal,
|
|
1004
|
+
onSubstitution: (substitution, context) => {
|
|
1005
|
+
options.onDiagnosticCounted();
|
|
1006
|
+
options.reporter.report(substitutionToDiagnostic(substitution, context.pageIndex));
|
|
1007
|
+
},
|
|
1008
|
+
formulas,
|
|
1009
|
+
fonts
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
async function runOdbRenderReport(input, output, options) {
|
|
1013
|
+
const command = "odb-render-report";
|
|
1014
|
+
if (output !== void 0 && options.out !== void 0 && output !== options.out) {
|
|
1015
|
+
process.stderr.write(`[${command}] conflicting output destinations: positional '${output}' and --out '${options.out}'\n`);
|
|
1016
|
+
return 2;
|
|
1017
|
+
}
|
|
1018
|
+
const target = resolveTargetFormat(output, options.out, options.to);
|
|
1019
|
+
if ("errorMessage" in target) {
|
|
1020
|
+
process.stderr.write(`[${command}] ${target.errorMessage}\n`);
|
|
1021
|
+
return 2;
|
|
1022
|
+
}
|
|
1023
|
+
if (!isOdbReportTargetFormat(target.format)) {
|
|
1024
|
+
process.stderr.write(`[${command}] '${target.format}' is not a supported report render target; expected one of docx, odt, pdf\n`);
|
|
1025
|
+
return 2;
|
|
1026
|
+
}
|
|
1027
|
+
const targetFormat = target.format;
|
|
1028
|
+
const resolvedOutput = output ?? options.out ?? (input === "-" ? "-" : resolveDefaultOutputPath(input, targetFormat));
|
|
1029
|
+
const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
|
|
1030
|
+
const reporter = createDiagnosticReporter({
|
|
1031
|
+
json: options.json,
|
|
1032
|
+
quiet: options.quiet,
|
|
1033
|
+
command
|
|
1034
|
+
});
|
|
1035
|
+
let diagnosticCount = 0;
|
|
1036
|
+
const reportFontSubstitution = options.reportFontSubstitutions === true ? createFontSubstitutionReporter({
|
|
1037
|
+
json: options.json,
|
|
1038
|
+
quiet: options.quiet,
|
|
1039
|
+
command
|
|
1040
|
+
}) : void 0;
|
|
1041
|
+
try {
|
|
1042
|
+
const inputBytes = await readInput(input, { signal });
|
|
1043
|
+
const fonts = await loadProvidedFonts(options.fontFile ?? [], { signal });
|
|
1044
|
+
const pkg = decodePackage(new Uint8Array(inputBytes));
|
|
1045
|
+
const bytes = renderOdbReportBytes(readOdbReportContent(pkg, { report: options.report }), targetFormat, {
|
|
1046
|
+
fonts,
|
|
1047
|
+
signal,
|
|
1048
|
+
reporter,
|
|
1049
|
+
reportFontSubstitution,
|
|
1050
|
+
onDiagnosticCounted: () => {
|
|
1051
|
+
diagnosticCount += 1;
|
|
1052
|
+
}
|
|
1053
|
+
});
|
|
1054
|
+
await writeOutput(resolvedOutput, bytes);
|
|
1055
|
+
reporter.summarize({
|
|
1056
|
+
output: resolvedOutput,
|
|
1057
|
+
bytes: bytes.byteLength,
|
|
1058
|
+
diagnosticCount
|
|
1059
|
+
});
|
|
1060
|
+
return 0;
|
|
1061
|
+
} catch (error) {
|
|
1062
|
+
return reportOdbReportError(command, error, options.verbose, getAbortReason());
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
907
1065
|
function registerOdbToXlsxCommand(program) {
|
|
908
1066
|
const command = program.command("odb-to-xlsx <input> [output]").description("extract every table an embedded .odb database declares into one xlsx workbook, one sheet per table");
|
|
909
1067
|
addOutOption(command);
|
|
@@ -942,12 +1100,33 @@ function registerOdbReportsCommand(program) {
|
|
|
942
1100
|
process.exitCode = await runOdbReports(input, options);
|
|
943
1101
|
});
|
|
944
1102
|
}
|
|
1103
|
+
function registerOdbQueryCommand(program) {
|
|
1104
|
+
program.command("odb-query <input>").description("run a bounded SELECT over an embedded .odb database's own extracted tables, given directly or by naming one of its saved queries").option("--sql <text>", "the SELECT statement to run -- mutually exclusive with --query").option("--query <savedName>", "the name of one of the .odb's own saved queries to run -- mutually exclusive with --sql").option("--json", "emit the result set as JSON ({ columns, rows }) instead of a plain-text table", false).action(async (input, options) => {
|
|
1105
|
+
process.exitCode = await runOdbQuery(input, options);
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
function registerOdbRenderReportCommand(program) {
|
|
1109
|
+
const command = program.command("odb-render-report <input> [output]").description("render one of an .odb's own reports -- its query resolved, its rpt: formulas evaluated, its bands laid out -- to docx, odt, or pdf");
|
|
1110
|
+
addOutOption(command);
|
|
1111
|
+
addTimeoutOption(command);
|
|
1112
|
+
addJsonOption(command);
|
|
1113
|
+
addQuietOption(command);
|
|
1114
|
+
addVerboseOption(command);
|
|
1115
|
+
addFontOptions(command);
|
|
1116
|
+
command.option("--report <name>", "the report to render -- required only when the .odb declares more than one report");
|
|
1117
|
+
command.option("--to <format>", "target format when it cannot be inferred from the output path (docx, odt, pdf)");
|
|
1118
|
+
command.action(async (input, output, options) => {
|
|
1119
|
+
process.exitCode = await runOdbRenderReport(input, output, options);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
945
1122
|
function registerOdbCommands(program) {
|
|
946
1123
|
registerOdbToXlsxCommand(program);
|
|
947
1124
|
registerOdbToCsvCommand(program);
|
|
948
1125
|
registerOdbTablesCommand(program);
|
|
949
1126
|
registerOdbFormsCommand(program);
|
|
950
1127
|
registerOdbReportsCommand(program);
|
|
1128
|
+
registerOdbQueryCommand(program);
|
|
1129
|
+
registerOdbRenderReportCommand(program);
|
|
951
1130
|
}
|
|
952
1131
|
//#endregion
|
|
953
1132
|
//#region src/commands/odm.ts
|
|
@@ -1128,7 +1307,7 @@ function registerPdfInspectCommand(program) {
|
|
|
1128
1307
|
}
|
|
1129
1308
|
//#endregion
|
|
1130
1309
|
//#region package.json
|
|
1131
|
-
var version = "1.
|
|
1310
|
+
var version = "1.3.0";
|
|
1132
1311
|
//#endregion
|
|
1133
1312
|
//#region src/program.ts
|
|
1134
1313
|
function createProgram() {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { d as inferFormatFromExtension, i as formatOdbReportLines, l as loadProvidedFonts, n as describeOdbReport, o as readInput, r as formatOdbFormLines, t as describeOdbForm, u as formatToExtension } from "./odb-structure-CX_0zL8_.js";
|
|
2
2
|
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import { createDocx, createOdg, createOdp, createOds, createOdt, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, openPptx, pptxToPdf, readOdbForms, readOdbReports, readOdbTables, readOdgContent, readOdsContent, readPdf, rgbHexToColor, xlsxToPdf } from "documents.js";
|
|
3
|
+
import { buildDocxPackage, buildOdtPackage, convertWordprocessingToLayout, createDocx, createFontMeasurer, createFontRegistry, createOdg, createOdp, createOds, createOdt, createPptx, decodeMarkdownText, docxToPdf, encodeMarkdownText, encodePackage, hsqldbCellDisplayText, markdownToPdf, odgToPdf, odpToPdf, odsToPdf, odtToPdf, openDocx, openOdg, openOdp, openOds, openOdt, openPptx, pptxToPdf, readOdbForms, readOdbReportContent, readOdbReports, readOdbTables, readOdgContent, readOdsContent, readPdf, rgbHexToColor, writePdf, xlsxToPdf } from "documents.js";
|
|
4
4
|
import { basename, dirname, extname, join } from "node:path";
|
|
5
|
-
import { cellReference, columnIndexToLetters, decodePackage } from "odf.js";
|
|
5
|
+
import { cellReference, columnIndexToLetters, decodePackage, encodePackage as encodePackage$1 } from "odf.js";
|
|
6
6
|
import { readdirSync } from "node:fs";
|
|
7
7
|
import { Box, Text, render, useApp, useInput, useWindowSize } from "ink";
|
|
8
8
|
import { createContext, useContext, useEffect, useReducer, useState } from "react";
|
|
@@ -238,6 +238,7 @@ function selectionKeyFor(screen) {
|
|
|
238
238
|
case "odbTableRows": return `odbTableRows:${screen.tableName}`;
|
|
239
239
|
case "odbFormDetail": return `odbFormDetail:${screen.formName}`;
|
|
240
240
|
case "odbReportDetail": return `odbReportDetail:${screen.reportName}`;
|
|
241
|
+
case "odbReportRender": return `odbReportRender:${screen.reportName}`;
|
|
241
242
|
case "markdownLineEditor": return `markdownLineEditor:${screen.lineIndex}`;
|
|
242
243
|
}
|
|
243
244
|
}
|
|
@@ -2537,7 +2538,15 @@ function OdbReportDetailScreen() {
|
|
|
2537
2538
|
const lines = query === "" ? allLines : allLines.filter((line) => line.toLowerCase().includes(query));
|
|
2538
2539
|
const { selectedIndex } = useNavigationInput({
|
|
2539
2540
|
itemCount: lines.length,
|
|
2540
|
-
onSelect: () => {
|
|
2541
|
+
onSelect: () => {
|
|
2542
|
+
dispatch({
|
|
2543
|
+
type: "PUSH_SCREEN",
|
|
2544
|
+
screen: {
|
|
2545
|
+
kind: "odbReportRender",
|
|
2546
|
+
reportName: report.name
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
},
|
|
2541
2550
|
onBack: () => {
|
|
2542
2551
|
dispatch({ type: "POP_SCREEN" });
|
|
2543
2552
|
},
|
|
@@ -2574,7 +2583,7 @@ function OdbReportDetailScreen() {
|
|
|
2574
2583
|
}),
|
|
2575
2584
|
/* @__PURE__ */ jsx(Text, {
|
|
2576
2585
|
dimColor: true,
|
|
2577
|
-
children: "Esc to go back to the report list"
|
|
2586
|
+
children: "Enter to render this report, Esc to go back to the report list"
|
|
2578
2587
|
})
|
|
2579
2588
|
]
|
|
2580
2589
|
});
|
|
@@ -2636,6 +2645,146 @@ function OdbReportListScreen() {
|
|
|
2636
2645
|
});
|
|
2637
2646
|
}
|
|
2638
2647
|
//#endregion
|
|
2648
|
+
//#region src/tui/format/render-odb-report.ts
|
|
2649
|
+
const REPORT_RENDER_TARGET_FORMATS = {
|
|
2650
|
+
docx: true,
|
|
2651
|
+
odt: true,
|
|
2652
|
+
pdf: true
|
|
2653
|
+
};
|
|
2654
|
+
function isReportRenderTargetFormat(format) {
|
|
2655
|
+
return format !== void 0 && format in REPORT_RENDER_TARGET_FORMATS;
|
|
2656
|
+
}
|
|
2657
|
+
function renderReportBytes(content, format, fonts, options) {
|
|
2658
|
+
if (format === "docx") return encodePackage(buildDocxPackage(content));
|
|
2659
|
+
if (format === "odt") return encodePackage$1(buildOdtPackage(content));
|
|
2660
|
+
if (content.kind !== "wordprocessing") throw new Error("readOdbReportContent returned a non-wordprocessing ContentDocument");
|
|
2661
|
+
const registry = createFontRegistry({
|
|
2662
|
+
fonts,
|
|
2663
|
+
onSubstitution: (substitution) => {
|
|
2664
|
+
const requested = `${substitution.requestedFamily}${substitution.requestedBold ? " bold" : ""}${substitution.requestedItalic ? " italic" : ""}`;
|
|
2665
|
+
options.onDiagnostic({
|
|
2666
|
+
severity: "info",
|
|
2667
|
+
message: `No "${requested}" face available; drew it in "${substitution.resolvedFamily}"`
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
});
|
|
2671
|
+
const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(registry) });
|
|
2672
|
+
return writePdf(layout, {
|
|
2673
|
+
signal: options.signal,
|
|
2674
|
+
onSubstitution: (substitution, context) => {
|
|
2675
|
+
options.onDiagnostic({
|
|
2676
|
+
severity: "info",
|
|
2677
|
+
message: `Substituted "${substitution.to}" for "${substitution.from}"`,
|
|
2678
|
+
pageIndex: context.pageIndex
|
|
2679
|
+
});
|
|
2680
|
+
},
|
|
2681
|
+
formulas,
|
|
2682
|
+
fonts: registry
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
async function renderOdbReportTo(doc, destinationPath, options) {
|
|
2686
|
+
const format = detectFormat(destinationPath);
|
|
2687
|
+
if (!isReportRenderTargetFormat(format)) throw new Error(`Cannot tell whether to render "${options.reportName}" as docx, odt, or pdf from '${destinationPath}' -- give the destination one of those three extensions`);
|
|
2688
|
+
const fonts = await loadProvidedFonts(options.fontFiles ?? [], { signal: options.signal });
|
|
2689
|
+
const pkg = decodePackage(new Uint8Array(await readFile(doc.path)));
|
|
2690
|
+
const bytes = renderReportBytes(readOdbReportContent(pkg, { report: options.reportName }), format, fonts, options);
|
|
2691
|
+
await writeFile(destinationPath, bytes);
|
|
2692
|
+
}
|
|
2693
|
+
//#endregion
|
|
2694
|
+
//#region src/tui/screens/editors/odb/report-render.tsx
|
|
2695
|
+
function defaultReportRenderDestination(odbPath, reportName) {
|
|
2696
|
+
return join(dirname(odbPath), `${reportName}.pdf`);
|
|
2697
|
+
}
|
|
2698
|
+
function parseFontFileField$1(value) {
|
|
2699
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
2700
|
+
}
|
|
2701
|
+
function OdbReportRenderScreen() {
|
|
2702
|
+
const state = useAppState();
|
|
2703
|
+
const dispatch = useAppDispatch();
|
|
2704
|
+
const isActive = !anyOverlayOpen(state);
|
|
2705
|
+
const doc = requireOdbDocument(state.openDocument);
|
|
2706
|
+
const screen = currentScreen(state);
|
|
2707
|
+
if (screen.kind !== "odbReportRender") throw new Error(`OdbReportRenderScreen rendered while the current screen is "${screen.kind}", not "odbReportRender".`);
|
|
2708
|
+
const reportName = screen.reportName;
|
|
2709
|
+
const [destination, setDestination] = useState(() => defaultReportRenderDestination(doc.path, reportName));
|
|
2710
|
+
const [fontFiles, setFontFiles] = useState("");
|
|
2711
|
+
const [field, setField] = useState("destination");
|
|
2712
|
+
const cancel = () => {
|
|
2713
|
+
dispatch({ type: "POP_SCREEN" });
|
|
2714
|
+
};
|
|
2715
|
+
const submit = () => {
|
|
2716
|
+
(async () => {
|
|
2717
|
+
let diagnosticCount = 0;
|
|
2718
|
+
try {
|
|
2719
|
+
await renderOdbReportTo(doc, destination, {
|
|
2720
|
+
reportName,
|
|
2721
|
+
fontFiles: parseFontFileField$1(fontFiles),
|
|
2722
|
+
onDiagnostic: (diagnostic) => {
|
|
2723
|
+
diagnosticCount += 1;
|
|
2724
|
+
dispatch({
|
|
2725
|
+
type: "APPEND_DIAGNOSTIC",
|
|
2726
|
+
diagnostic
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
});
|
|
2730
|
+
dispatch({
|
|
2731
|
+
type: "SET_STATUS",
|
|
2732
|
+
severity: "info",
|
|
2733
|
+
text: `Rendered "${reportName}" to ${destination}`
|
|
2734
|
+
});
|
|
2735
|
+
if (diagnosticCount > 0) dispatch({
|
|
2736
|
+
type: "OPEN_OVERLAY",
|
|
2737
|
+
overlay: "diagnosticsPanel"
|
|
2738
|
+
});
|
|
2739
|
+
dispatch({ type: "POP_SCREEN" });
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
dispatch({
|
|
2742
|
+
type: "OPEN_FILE_ERROR",
|
|
2743
|
+
message: `Could not render "${reportName}" to ${destination}`,
|
|
2744
|
+
detail: describeError(error)
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
})();
|
|
2748
|
+
};
|
|
2749
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2750
|
+
flexDirection: "column",
|
|
2751
|
+
children: [
|
|
2752
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
2753
|
+
bold: true,
|
|
2754
|
+
children: ["Render report: ", reportName]
|
|
2755
|
+
}),
|
|
2756
|
+
/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2757
|
+
color: "cyan",
|
|
2758
|
+
children: "Path: "
|
|
2759
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2760
|
+
value: destination,
|
|
2761
|
+
isFocused: isActive && field === "destination",
|
|
2762
|
+
placeholder: "destination path (.docx, .odt, or .pdf)",
|
|
2763
|
+
onChange: setDestination,
|
|
2764
|
+
onSubmit: () => {
|
|
2765
|
+
setField("fonts");
|
|
2766
|
+
},
|
|
2767
|
+
onCancel: cancel
|
|
2768
|
+
})] }),
|
|
2769
|
+
/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
|
|
2770
|
+
color: "cyan",
|
|
2771
|
+
children: "Fonts: "
|
|
2772
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2773
|
+
value: fontFiles,
|
|
2774
|
+
isFocused: isActive && field === "fonts",
|
|
2775
|
+
placeholder: "optional .ttf/.otf paths, comma-separated -- pdf only",
|
|
2776
|
+
onChange: setFontFiles,
|
|
2777
|
+
onSubmit: submit,
|
|
2778
|
+
onCancel: cancel
|
|
2779
|
+
})] }),
|
|
2780
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2781
|
+
dimColor: true,
|
|
2782
|
+
children: field === "destination" ? "Enter for fonts, Esc to cancel" : "Enter to render, Esc to cancel. The destination's own extension picks docx/odt/pdf"
|
|
2783
|
+
})
|
|
2784
|
+
]
|
|
2785
|
+
});
|
|
2786
|
+
}
|
|
2787
|
+
//#endregion
|
|
2639
2788
|
//#region src/tui/screens/editors/odb/table-list.tsx
|
|
2640
2789
|
const TABLE_LIST_RESERVED_ROWS = 5;
|
|
2641
2790
|
function OdbTableListScreen() {
|
|
@@ -6061,6 +6210,7 @@ function ScreenBody({ screen }) {
|
|
|
6061
6210
|
case "odbFormDetail": return /* @__PURE__ */ jsx(OdbFormDetailScreen, {});
|
|
6062
6211
|
case "odbReportList": return /* @__PURE__ */ jsx(OdbReportListScreen, {});
|
|
6063
6212
|
case "odbReportDetail": return /* @__PURE__ */ jsx(OdbReportDetailScreen, {});
|
|
6213
|
+
case "odbReportRender": return /* @__PURE__ */ jsx(OdbReportRenderScreen, {});
|
|
6064
6214
|
case "markdownLineList": return /* @__PURE__ */ jsx(MarkdownLineListScreen, {});
|
|
6065
6215
|
case "markdownLineEditor": return /* @__PURE__ */ jsx(MarkdownLineEditorScreen, {});
|
|
6066
6216
|
case "pdfPageList": return /* @__PURE__ */ jsx(PdfPageListScreen, {});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "document-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "CLI and interactive Ink TUI for documents.js: every docx/pptx/odt/odp/ods/odg/odf/pdf/odm/odb/xlsx/markdown conversion, bridge, and editor as a scriptable command or a terminal app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|