document-cli 1.10.0 → 1.11.1

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/README.md CHANGED
@@ -41,7 +41,7 @@ graph TD
41
41
 
42
42
  ## Why
43
43
 
44
- `documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's 27 direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions, and its PDF inspector become a single command-line invocation, and its six live-view editors (docx/pptx/odt/odp/ods/odg) plus its line-based markdown editor become a keyboard-driven terminal app that never needs a code editor open at all.
44
+ `documents.js` is a library, not a tool — everything it does happens through function calls from TypeScript/JavaScript. Most people who want to convert a docx to a PDF, extract an `.odb` table to CSV, or poke at a PDF's structure from a terminal don't want to write a script to do it. `document-cli` is that missing entry point: every one of documents.js's 27 direct conversion pairs, its generic converter, its `.odm`/`.odb` extraction functions, and its PDF inspector become a single command-line invocation, and its seven live-view editors (docx/pptx/odt/odp/ods/odg/markdown) become a keyboard-driven terminal app that never needs a code editor open at all.
45
45
 
46
46
  The CLI and the TUI are deliberately not two separate implementations of the same logic. The TUI's own document-opening, saving, and PDF-export code (`src/tui/format/`) calls the identical `documents.js` functions the CLI commands call — `openDocx`/`createDocx`/`docxToPdf` and their five siblings per format, plus `readOdbTables`/`readPdf` for the two read-only sources — so there is exactly one place either surface can drift from what documents.js itself does: nowhere.
47
47
 
@@ -165,9 +165,9 @@ Diagnostics and the summary line always go to stderr; stdout is reserved for the
165
165
 
166
166
  Launch it either bare (`document-cli`, with no arguments) or explicitly with `document-cli tui [file]` — both open the same app; the explicit form additionally opens `file` immediately, skipping the launcher screen. The TUI needs an interactive terminal: a bare invocation with redirected stdout prints help text instead, and an explicit `tui` invocation with redirected stdout fails outright, since there's no terminal for Ink to draw into.
167
167
 
168
- It supports the same six formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt, slides and shapes for pptx/odp, sheets and cells for ods, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate.
168
+ It supports the same seven formats documents.js's live-view editors cover — docx, pptx, odt, odp, ods, odg, markdown — each with a full navigate/edit/save experience built on that format's editor (paragraphs and runs for docx/odt/markdown, slides and shapes for pptx/odp, sheets and cells for ods, pages and vectors/shapes for odg), plus undo (whole-document snapshots taken before each committed mutation), search, a command palette, and PDF export straight from the open document. On a pptx or odp slide, `a` from the shape list also adds a real table (rows then columns, a two-step prompt) alongside the existing textbox/image choices, and `n` opens the slide's own speaker notes for either format — `PptxSlide` and `OdpSlide` both carry a real `.notes` getter/setter, so notes editing was never odp-specific, only gated that way until this phase removed the gate.
169
169
 
170
- Markdown (`.md`/`.markdown`) is also a fully supported TUI format, but a structurally different one: documents.js has no `MarkdownEditor` the way it has a `DocxEditor`/`OdtEditor` (its own markdown support is a thin read/write pair over a plain string, not an `XmlElement` tree to hold a live view into), so the TUI edits a markdown document as its own raw source text rather than through a live-view editor object. Opening a `.md` file loads its bytes verbatim as a source string (`decodeMarkdownText`); the root screen lists that source split into lines, one row each, with search filtering by line content; selecting a line opens a line editor that replaces its text in place; `Ctrl+S` writes the (possibly edited) source straight back to disk (`encodeMarkdownText`), the identical byte↔text boundary opening it went through in reverse never through `readMarkdownContent`/`buildMarkdownText`. PDF export is the one place the `ContentDocument` pivot enters at all: exporting calls `markdownToPdf` directly on the current source text, run fresh on every export rather than kept in sync with in-progress edits. There is no "create a new markdown document" flow (documents.js has no `createMarkdown()`), so a markdown document can only be opened from an existing file, never created fresh from the TUI's new-document screen the way docx/pptx/odt/odp/ods/odg can.
170
+ Markdown (`.md`/`.markdown`) shares the same paragraph/run/table body-list screens docx and odt already use, through documents.js's own `MarkdownEditor` (`openMarkdown`/`createMarkdownEditor`) — a genuine live view over a mutable `ContentDocument`, the same live-view contract every other editor here follows, even though there is no `XmlElement` tree underneath it the way there is for docx/odt (`MarkdownEditor.toMarkdownText()` re-serialises the whole document fresh on every call, rather than exposing a `toBytes()`). Appending a paragraph, appending a run, and toggling bold/italic all go through the identical reducer actions docx/odt use; a markdown run has no underline, colour, font family, or font size at all (CommonMark/GFM has no construct for any of the four), so those keys along with image insertion, which `MarkdownParagraph` has no counterpart for — are simply absent from a markdown paragraph's own key hints rather than opening a prompt that could only end in a warning. A markdown table can be created and its cells edited through the same 'T' wizard and table-view screens docx/odt use, but GFM tables have no cell-merge concept, so a merge requested alongside table creation still creates the table (unmerged) and reports why the merge itself didn't happen. `:view-source` (markdown documents only) shows the literal text the document was opened with side by side with what a save would write right now — these can genuinely differ even with no edits made this session, from a heading-style, bullet-marker, or line-ending choice the writer normalises. Every save re-serialises the whole document fresh through `buildMarkdownText`, a deliberate, permanent consequence of structured editing rather than something to work around. Diagnostics from the read side (a clamped heading level, a dropped front-matter key, a fenced code block's own info string with nowhere to go, …) now surface into the same diagnostics panel a PDF export's own substitutions already populate, the moment a `.md` file is opened, not only on export. documents.js's own `createMarkdownEditor()` exists now, but this TUI does not yet wire a "new markdown document" flow into `:new`/the new-document picker, so a markdown document can still only be opened from an existing file.
171
171
 
172
172
  Three further formats open read-only: a `.odb` browses its tables and rows with no write path at all (documents.js's own `.odb` support has no write direction to offer), a `.pdf` browses its pages and positioned items rather than being edited in place, and a `.xlsx` opens as a converted PDF preview — documents.js has no xlsx editor to hold a live view into, so opening one runs `xlsxToPdf` once at open time and browses the result through the identical page-list/page-items/item-detail screens a real `.pdf` uses, with the original bytes kept alongside so a later export re-runs `xlsxToPdf` with the caller's own fonts and diagnostics rather than reusing the fixed preview conversion. A `.odb` additionally browses its *structure* alongside its data: `f` from the table list opens the form browser and `r` the report browser, each listing what the database declares and opening one to show it in full — a form's own data source and field-bound controls (sub-forms nested under their parent), a report's data-source command, band and group structure, and every `rpt:` formula. Both are rendered through the same `src/odb-structure.ts` the `odb-forms`/`odb-reports` commands print, so the two views cannot drift apart, and search filters by line (`/SUM` narrows a long report to its aggregate expressions). A standalone `.odf` formula document has no TUI editor either — nothing to edit interactively, only a PDF conversion.
173
173
 
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { a as odbFormSummary, c as formatDocxExtrasLines, d as writeOutput, f as loadProvidedFonts, h as isDocumentFormat, i as formatOdbReportLines, l as readInput, m as inferFormatFromExtension, n as describeOdbReport, o as formatMetadataLines, r as formatOdbFormLines, s as presentMetadataEntries, t as describeOdbForm, u as resolveDefaultOutputPath } from "./odb-structure-Du1w_hOU.js";
2
+ import { a as odbFormSummary, c as formatDocxExtrasLines, d as writeOutput, f as loadProvidedFonts, h as isDocumentFormat, i as formatOdbReportLines, l as readInput, m as inferFormatFromExtension, n as describeOdbReport, o as formatMetadataLines, r as formatOdbFormLines, s as presentMetadataEntries, t as describeOdbForm, u as resolveDefaultOutputPath } from "./odb-structure-CS2Eybob.js";
3
3
  import { Command, CommanderError, InvalidArgumentError } from "commander";
4
4
  import { writeFile } from "node:fs/promises";
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, decodeMarkdownText, decodePackage, documentFromJson, documentPackageWithSchema, encodeMarkdownText, encodePackage, evaluateSelect, extractSourceFonts, hsqldbCellDisplayText, layoutDocumentWithSchema, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readDocxContent, readDocxExtras, readMarkdownContent, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readOdfFormulaContent, readOdgContent, readOdpContent, readOdsContent, readOdtContent, readPdf, readPptxContent, writePdf, xlsxToPdf } from "documents.js";
5
+ import { HsqldbSqlEvaluationError, HsqldbSqlParseError, HsqldbSqlUnsupportedError, OdbNoEmbeddedDataSourceError, OdbReportNotSpecifiedError, OdbTableNotFoundError, OdbTableNotSpecifiedError, OdbUnsupportedFormatError, OdmUnresolvedSectionError, PdfEncryptedError, PdfParseError, UnrecognizedDocumentSchemaError, UnsupportedFontSourceFormatError, buildDocumentBytes, createLocalDocumentConverter, decodePackage, documentFromJson, documentPackageWithSchema, evaluateSelect, extractSourceFontsForFormat, hsqldbCellDisplayText, layoutDocumentWithSchema, odbReportToDocx, odbReportToOdt, odbReportToPdf, odbToCsv, odbToXlsx, odmToPdf, parseSelect, readDocumentMetadata, readDocxExtras, readOdbForms, readOdbInventory, readOdbReportContent, readOdbReports, readOdbTables, readPdf, setDocumentMetadata } from "documents.js";
6
6
  import { basename, dirname, extname, join } from "node:path";
7
- import { decodePackage as decodePackage$1, encodePackage as encodePackage$1 } from "odf.js";
7
+ import { decodePackage as decodePackage$1 } from "odf.js";
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  //#region src/runtime/abort.ts
10
10
  function combineSignals(a, b) {
@@ -116,6 +116,7 @@ function mapErrorToExit(error, abortReason) {
116
116
  if (abortReason === "timeout") return 124;
117
117
  if (error instanceof OdmUnresolvedSectionError || error instanceof OdbTableNotSpecifiedError || error instanceof OdbTableNotFoundError || error instanceof OdbNoEmbeddedDataSourceError || error instanceof OdbUnsupportedFormatError || error instanceof OdbReportNotSpecifiedError) return 3;
118
118
  if (error instanceof HsqldbSqlUnsupportedError || error instanceof HsqldbSqlParseError || error instanceof HsqldbSqlEvaluationError) return 1;
119
+ if (error instanceof UnsupportedFontSourceFormatError) return 2;
119
120
  if (error instanceof PdfEncryptedError || error instanceof PdfParseError) return 1;
120
121
  return 1;
121
122
  }
@@ -306,27 +307,6 @@ function registerDocxExtrasCommand(program) {
306
307
  }
307
308
  //#endregion
308
309
  //#region src/commands/fonts.ts
309
- const FONT_SOURCE_FORMATS = {
310
- docx: true,
311
- pptx: true,
312
- odt: true,
313
- odp: true,
314
- ods: true,
315
- odg: true
316
- };
317
- function isFontSourceFormat(format) {
318
- return format in FONT_SOURCE_FORMATS;
319
- }
320
- function resolveFontSourcePackage(format, bytes) {
321
- if (format === "docx" || format === "pptx") return {
322
- kind: format,
323
- package: decodePackage(bytes)
324
- };
325
- return {
326
- kind: "odf",
327
- package: decodePackage$1(bytes)
328
- };
329
- }
330
310
  async function runFonts(input, options) {
331
311
  const command = "fonts";
332
312
  const { signal, getAbortReason } = createRuntimeSignal({});
@@ -336,13 +316,8 @@ async function runFonts(input, options) {
336
316
  process.stderr.write(`[${command}] cannot infer a document format from '${input}'; expected one of docx, pptx, odt, odp, ods, odg\n`);
337
317
  return 2;
338
318
  }
339
- if (!isFontSourceFormat(format)) {
340
- process.stderr.write(`[${command}] '${format}' documents carry no source-embedded font faces this command can extract; expected one of docx, pptx, odt, odp, ods, odg\n`);
341
- return 2;
342
- }
343
319
  const inputBytes = await readInput(input, { signal });
344
- const source = resolveFontSourcePackage(format, new Uint8Array(inputBytes));
345
- const summaries = extractSourceFonts(source).map((face) => ({
320
+ const summaries = extractSourceFontsForFormat(format, new Uint8Array(inputBytes)).map((face) => ({
346
321
  family: face.family,
347
322
  bold: face.bold,
348
323
  italic: face.italic,
@@ -387,23 +362,6 @@ function registerFormatsCommand(program) {
387
362
  }
388
363
  //#endregion
389
364
  //#region src/commands/from-package.ts
390
- function buildBytesForTarget(pkg, target) {
391
- if (target === "pdf") {
392
- if (pkg.layout === void 0) throw new Error("this DocumentPackage has no layout -- only a package dumped from a <format>-to-pdf or pdf-to-<format> conversion carries one; a bridge conversion's own dump (e.g. odt-to-docx) never does, so 'pdf' is not a reachable target from it");
393
- return writePdf(pkg.layout);
394
- }
395
- switch (target) {
396
- case "docx": return encodePackage(buildDocxPackage(pkg.content));
397
- case "pptx": return encodePackage(buildPptxPackage(pkg.content));
398
- case "odt": return encodePackage$1(buildOdtPackage(pkg.content));
399
- case "odp": return encodePackage$1(buildOdpPackage(pkg.content));
400
- case "ods": return encodePackage$1(buildOdsPackage(pkg.content));
401
- case "odg": return encodePackage$1(buildOdgPackage(pkg.content));
402
- case "markdown": return encodeMarkdownText(buildMarkdownText(pkg.content));
403
- case "xlsx": throw new Error("'xlsx' cannot be built from a DocumentPackage directly -- documents.js does not re-export a ContentDocument-to-xlsx builder; convert to 'ods' here, then run 'ods-to-xlsx' on the result instead");
404
- case "odf": throw new Error("'odf' (a standalone formula document) cannot be built from a DocumentPackage -- there is no ContentDocument-to-odf builder");
405
- }
406
- }
407
365
  async function runFromPackage(input, output, options) {
408
366
  const command = "from-package";
409
367
  if (output !== void 0 && options.out !== void 0 && output !== options.out) {
@@ -432,7 +390,7 @@ async function runFromPackage(input, output, options) {
432
390
  process.stderr.write(`[${command}] '${input}' is a ${result.kind}, not a DocumentPackage -- only a file written by --dump-package can be read back by this command\n`);
433
391
  return 2;
434
392
  }
435
- const bytes = buildBytesForTarget(result.value, target.format);
393
+ const bytes = buildDocumentBytes(result.value, target.format);
436
394
  await writeOutput(resolvedOutput, bytes);
437
395
  createDiagnosticReporter({
438
396
  json: options.json,
@@ -467,20 +425,6 @@ function registerFromPackageCommand(program) {
467
425
  }
468
426
  //#endregion
469
427
  //#region src/commands/metadata.ts
470
- function readMetadataForFormat(format, bytes, signal) {
471
- switch (format) {
472
- case "docx": return readDocxContent(decodePackage(bytes)).metadata;
473
- case "pptx": return readPptxContent(decodePackage(bytes)).metadata;
474
- case "odt": return readOdtContent(decodePackage$1(bytes)).metadata;
475
- case "odp": return readOdpContent(decodePackage$1(bytes)).metadata;
476
- case "ods": return readOdsContent(decodePackage$1(bytes)).metadata;
477
- case "odg": return readOdgContent(decodePackage$1(bytes)).metadata;
478
- case "odf": return readOdfFormulaContent(decodePackage$1(bytes)).metadata;
479
- case "markdown": return readMarkdownContent(decodeMarkdownText(bytes)).metadata;
480
- case "pdf": return readPdf(bytes, { signal }).metadata;
481
- case "xlsx": return readPdf(xlsxToPdf(bytes, { signal }), { signal }).metadata;
482
- }
483
- }
484
428
  async function runMetadata(input, options) {
485
429
  const command = "metadata";
486
430
  const source = inferFormatFromExtension(input);
@@ -491,7 +435,7 @@ async function runMetadata(input, options) {
491
435
  const { signal, getAbortReason } = createRuntimeSignal({});
492
436
  try {
493
437
  const inputBytes = await readInput(input, { signal });
494
- const metadata = readMetadataForFormat(source, new Uint8Array(inputBytes), signal);
438
+ const metadata = readDocumentMetadata(source, new Uint8Array(inputBytes), { signal });
495
439
  if (options.json) {
496
440
  process.stdout.write(`${JSON.stringify(metadata)}\n`);
497
441
  return 0;
@@ -739,32 +683,6 @@ const ODB_REPORT_TARGET_FORMATS = {
739
683
  function isOdbReportTargetFormat(format) {
740
684
  return format in ODB_REPORT_TARGET_FORMATS;
741
685
  }
742
- function renderOdbReportBytes(content, target, options) {
743
- if (target === "docx") return encodePackage(buildDocxPackage(content));
744
- if (target === "odt") return encodePackage$1(buildOdtPackage(content));
745
- if (content.kind !== "wordprocessing") throw new Error("readOdbReportContent returned a non-wordprocessing ContentDocument");
746
- const fonts = createFontRegistry({
747
- fonts: options.fonts,
748
- onSubstitution: (substitution) => {
749
- if (options.reportFontSubstitution !== void 0) {
750
- options.reportFontSubstitution(substitution);
751
- return;
752
- }
753
- options.onDiagnosticCounted();
754
- options.reporter.report(fontSubstitutionToDiagnostic(substitution));
755
- }
756
- });
757
- const { document: layout, formulas } = convertWordprocessingToLayout(content, { measurer: createFontMeasurer(fonts) });
758
- return writePdf(layout, {
759
- signal: options.signal,
760
- onSubstitution: (substitution, context) => {
761
- options.onDiagnosticCounted();
762
- options.reporter.report(substitutionToDiagnostic(substitution, context.pageIndex));
763
- },
764
- formulas,
765
- fonts
766
- });
767
- }
768
686
  async function runOdbRenderReport(input, output, options) {
769
687
  const command = "odb-render-report";
770
688
  if (output !== void 0 && options.out !== void 0 && output !== options.out) {
@@ -798,13 +716,21 @@ async function runOdbRenderReport(input, output, options) {
798
716
  const inputBytes = await readInput(input, { signal });
799
717
  const fonts = await loadProvidedFonts(options.fontFile ?? [], { signal });
800
718
  const pkg = decodePackage$1(new Uint8Array(inputBytes));
801
- const bytes = renderOdbReportBytes(readOdbReportContent(pkg, { report: options.report }), targetFormat, {
802
- fonts,
719
+ const content = readOdbReportContent(pkg, { report: options.report });
720
+ const bytes = targetFormat === "docx" ? odbReportToDocx(content) : targetFormat === "odt" ? odbReportToOdt(content) : odbReportToPdf(content, {
803
721
  signal,
804
- reporter,
805
- reportFontSubstitution,
806
- onDiagnosticCounted: () => {
722
+ fonts,
723
+ onFontSubstitution: (substitution) => {
724
+ if (reportFontSubstitution !== void 0) {
725
+ reportFontSubstitution(substitution);
726
+ return;
727
+ }
728
+ diagnosticCount += 1;
729
+ reporter.report(fontSubstitutionToDiagnostic(substitution));
730
+ },
731
+ onSubstitution: (substitution, context) => {
807
732
  diagnosticCount += 1;
733
+ reporter.report(substitutionToDiagnostic(substitution, context.pageIndex));
808
734
  }
809
735
  });
810
736
  await writeOutput(resolvedOutput, bytes);
@@ -1047,63 +973,9 @@ function registerPdfInspectCommand(program) {
1047
973
  }
1048
974
  //#endregion
1049
975
  //#region src/commands/set-metadata.ts
1050
- const REBUILD_FORMATS = {
1051
- docx: true,
1052
- pptx: true,
1053
- odt: true,
1054
- odp: true,
1055
- ods: true,
1056
- odg: true,
1057
- markdown: true
1058
- };
1059
- function isRebuildFormat(format) {
1060
- return format in REBUILD_FORMATS;
1061
- }
1062
- function readContentForFormat(format, bytes) {
1063
- switch (format) {
1064
- case "docx": return readDocxContent(decodePackage(bytes));
1065
- case "pptx": return readPptxContent(decodePackage(bytes));
1066
- case "odt": return readOdtContent(decodePackage$1(bytes));
1067
- case "odp": return readOdpContent(decodePackage$1(bytes));
1068
- case "ods": return readOdsContent(decodePackage$1(bytes));
1069
- case "odg": return readOdgContent(decodePackage$1(bytes));
1070
- case "markdown": return readMarkdownContent(decodeMarkdownText(bytes));
1071
- }
1072
- }
1073
- function buildBytesForRebuildFormat(format, content) {
1074
- switch (format) {
1075
- case "docx": return encodePackage(buildDocxPackage(content));
1076
- case "pptx": return encodePackage(buildPptxPackage(content));
1077
- case "odt": return encodePackage$1(buildOdtPackage(content));
1078
- case "odp": return encodePackage$1(buildOdpPackage(content));
1079
- case "ods": return encodePackage$1(buildOdsPackage(content));
1080
- case "odg": return encodePackage$1(buildOdgPackage(content));
1081
- case "markdown": return encodeMarkdownText(buildMarkdownText(content));
1082
- }
1083
- }
1084
- function mergeMetadata(current, overrides) {
1085
- return {
1086
- ...current,
1087
- ...overrides.title !== void 0 ? { title: overrides.title } : {},
1088
- ...overrides.author !== void 0 ? { author: overrides.author } : {},
1089
- ...overrides.subject !== void 0 ? { subject: overrides.subject } : {},
1090
- ...overrides.keywords !== void 0 ? { keywords: overrides.keywords } : {}
1091
- };
1092
- }
1093
976
  function parseKeywords(csv) {
1094
977
  return csv.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
1095
978
  }
1096
- function classifyWritePath(source, target) {
1097
- if (source === "pdf" && target === "pdf") return { kind: "pdf" };
1098
- if (target === "xlsx" || source === "xlsx") return { errorMessage: "'xlsx' is not a supported set-metadata source or target -- documents.js does not re-export a ContentDocument-to-xlsx builder or a readXlsxContent from its own public surface (see that package's own README, Architecture section); convert with 'xlsx-to-ods'/'ods-to-xlsx' first, then set metadata on the ods" };
1099
- if (target === "odf" || source === "odf") return { errorMessage: "'odf' (a standalone formula document) is not a supported set-metadata source or target -- it has no write path back out at all" };
1100
- if (!isRebuildFormat(source) || !isRebuildFormat(target)) return { errorMessage: `set-metadata only patches metadata in place; it does not convert format -- source ('${source}') and target ('${target}') must be the same format (or both 'pdf'). Run 'convert'/'from-package' first if you need a different target format.` };
1101
- if (source !== target) return { errorMessage: `set-metadata only patches metadata in place; it does not convert format -- source ('${source}') and target ('${target}') must be the same format. Run 'convert'/'from-package' first if you need a different target format.` };
1102
- return {
1103
- kind: "rebuild",
1104
- format: source
1105
- };
1106
- }
1107
979
  async function runSetMetadata(input, output, options) {
1108
980
  const command = "set-metadata";
1109
981
  if (output !== void 0 && options.out !== void 0 && output !== options.out) {
@@ -1120,11 +992,6 @@ async function runSetMetadata(input, output, options) {
1120
992
  process.stderr.write(`[${command}] cannot infer a source format from '${input}'; rename the file with a recognised extension (${KNOWN_DOCUMENT_FORMATS})\n`);
1121
993
  return 2;
1122
994
  }
1123
- const writePath = classifyWritePath(source, target.format);
1124
- if ("errorMessage" in writePath) {
1125
- process.stderr.write(`[${command}] ${writePath.errorMessage}\n`);
1126
- return 2;
1127
- }
1128
995
  const overrides = {
1129
996
  title: options.setTitle,
1130
997
  author: options.setAuthor,
@@ -1135,21 +1002,7 @@ async function runSetMetadata(input, output, options) {
1135
1002
  const { signal, getAbortReason } = createRuntimeSignal({ timeoutMs: options.timeout });
1136
1003
  try {
1137
1004
  const inputBytes = await readInput(input, { signal });
1138
- const bytes = writePath.kind === "pdf" ? (() => {
1139
- const layout = readPdf(new Uint8Array(inputBytes), { signal });
1140
- const patched = {
1141
- ...layout,
1142
- metadata: mergeMetadata(layout.metadata, overrides)
1143
- };
1144
- return writePdf(patched, { signal });
1145
- })() : (() => {
1146
- const content = readContentForFormat(writePath.format, new Uint8Array(inputBytes));
1147
- const nextContent = {
1148
- ...content,
1149
- metadata: mergeMetadata(content.metadata, overrides)
1150
- };
1151
- return buildBytesForRebuildFormat(writePath.format, nextContent);
1152
- })();
1005
+ const bytes = setDocumentMetadata(source, target.format, new Uint8Array(inputBytes), overrides, { signal });
1153
1006
  await writeOutput(resolvedOutput, bytes);
1154
1007
  createDiagnosticReporter({
1155
1008
  json: options.json,
@@ -1194,7 +1047,7 @@ function registerSetMetadataCommand(program) {
1194
1047
  }
1195
1048
  //#endregion
1196
1049
  //#region package.json
1197
- var version = "1.10.0";
1050
+ var version = "1.11.1";
1198
1051
  //#endregion
1199
1052
  //#region src/program.ts
1200
1053
  function createProgram() {
@@ -1221,7 +1074,7 @@ function createProgram() {
1221
1074
  //#region src/cli.ts
1222
1075
  async function launchTui(startPath, signal) {
1223
1076
  try {
1224
- const { runTui } = await import("./tui-BVeYd0ry.js");
1077
+ const { runTui } = await import("./tui-Bw_z7xq6.js");
1225
1078
  await runTui({
1226
1079
  startPath,
1227
1080
  signal