document-operations 1.1.50 → 1.1.52

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/index.js CHANGED
@@ -10,13 +10,13 @@ import { buildOutline, isOutlineNode, outlineLeafText } from "document-outline.j
10
10
  /**
11
11
  * Builds a DocumentOperation whose input and output types are both inferred from real Zod schemas, so a call site never has to repeat `z.infer<typeof Schema>` itself. Use `defineOperationWithoutOutputSchema` instead for the rare operation whose original MCP tool registration declared no outputSchema at all.
12
12
  *
13
- * Deliberately returns the parameter object's own inferred literal type rather than an explicit `DocumentOperation<...>` return annotation. Zod 4's ZodType carries its own output type as one of three generic parameters (ZodType<Output, Input, Internals>), and TypeScript cannot prove, from inside this function body, that a generic `InputSchema extends z.ZodType` also extends `z.ZodType<z.infer<InputSchema>>` -- the two-step indirection through an abstract type parameter defeats it even though it is true for every concrete schema. Letting the literal's own type flow out instead sidesteps that: a caller's own concrete schema (e.g. `ConvertDocumentInputSchema`) is trivially assignable to `DocumentOperation<In, Out>`'s `z.ZodType<In>` field wherever that supertype is actually needed (the registry array below, or a future MCP/REST/CLI adapter parameter), because widening a CONCRETE schema's output type to line up is a normal covariant check, not the same self-referential generic one.
13
+ * Deliberately returns the parameter object's own inferred literal type rather than an explicit `DocumentOperation<...>` return annotation. Zod 4's ZodType carries its own output type as one of three generic parameters (ZodType<Output, Input, Internals>), and TypeScript cannot prove, from inside this function body, that a generic `InputSchema extends z.ZodType` also extends `z.ZodType<z.infer<InputSchema>>` — the two-step indirection through an abstract type parameter defeats it even though it is true for every concrete schema. Letting the literal's own type flow out instead sidesteps that: a caller's own concrete schema (e.g. `ConvertDocumentInputSchema`) is trivially assignable to `DocumentOperation<In, Out>`'s `z.ZodType<In>` field wherever that supertype is actually needed (the registry array below, or a future MCP/REST/CLI adapter parameter), because widening a CONCRETE schema's output type to line up is a normal covariant check, not the same self-referential generic one.
14
14
  */
15
15
  function defineOperation(operation) {
16
16
  return operation;
17
17
  }
18
18
  /**
19
- * The `defineOperation` counterpart for an operation with no output schema to infer from (the original MCP tool registration declared none) -- `Out` is inferred from `run`'s own return type instead. See `defineOperation`'s own comment for why this returns the parameter object's inferred literal type rather than an explicit `DocumentOperation<...>` annotation.
19
+ * The `defineOperation` counterpart for an operation with no output schema to infer from (the original MCP tool registration declared none) — `Out` is inferred from `run`'s own return type instead. See `defineOperation`'s own comment for why this returns the parameter object's inferred literal type rather than an explicit `DocumentOperation<...>` annotation.
20
20
  */
21
21
  function defineOperationWithoutOutputSchema(operation) {
22
22
  return operation;
@@ -67,7 +67,7 @@ function inferFormatFromExtension(path) {
67
67
  */
68
68
  const DocumentInputSchema = z.union([z.object({ path: z.string().describe("Filesystem path to the document to read. The document format is inferred from the file extension.") }), z.object({
69
69
  bytesBase64: z.string().describe("Base64-encoded document bytes."),
70
- format: DocumentFormatSchema.describe("The document format of bytesBase64 -- required, since inline bytes carry no filename to infer it from.")
70
+ format: DocumentFormatSchema.describe("The document format of bytesBase64 — required, since inline bytes carry no filename to infer it from.")
71
71
  })]);
72
72
  /**
73
73
  * Resolves the hybrid DocumentInput union to concrete bytes and a format: for the path shape, reads the file from disk and infers its format from the extension (throwing if the extension is unrecognised); for the bytesBase64 shape, decodes the inline payload and uses the caller-supplied format directly.
@@ -94,7 +94,7 @@ async function resolveDocumentInput(input, options) {
94
94
  */
95
95
  const DocumentOutputSchema = z.object({ outputPath: z.string().optional().describe("Filesystem path to write the resulting document to. Omit to receive the document bytes inline, base64-encoded, in the tool result instead.") });
96
96
  /**
97
- * Above this many bytes, an inline base64 result is flagged `large: true` so a caller/LLM can see the response is sizeable before deciding whether to consume it directly. Purely advisory: `resolveDocumentOutput` never truncates or refuses to return large bytes, it only flags them -- silently truncating a document would produce a corrupt file with no indication anything was lost. 5 MB is a reasonable default order of magnitude for "an LLM context probably wants to know before this lands inline", well under typical MCP stdio transport limits.
97
+ * Above this many bytes, an inline base64 result is flagged `large: true` so a caller/LLM can see the response is sizeable before deciding whether to consume it directly. Purely advisory: `resolveDocumentOutput` never truncates or refuses to return large bytes, it only flags them — silently truncating a document would produce a corrupt file with no indication anything was lost. 5 MB is a reasonable default order of magnitude for "an LLM context probably wants to know before this lands inline", well under typical MCP stdio transport limits.
98
98
  */
99
99
  const LARGE_RESULT_THRESHOLD_BYTES = 5242880;
100
100
  /**
@@ -150,11 +150,11 @@ const FontInputSchema = z.object({
150
150
  });
151
151
  const ConvertDocumentInputSchema = z.object({
152
152
  source: DocumentInputSchema.describe("The document to convert."),
153
- targetFormat: DocumentFormatSchema.describe("The format to convert the document to. Not every (source, targetFormat) pair is supported directly -- call list_document_conversions first to confirm this one is."),
153
+ targetFormat: DocumentFormatSchema.describe("The format to convert the document to. Not every (source, targetFormat) pair is supported directly — call list_document_conversions first to confirm this one is."),
154
154
  output: DocumentOutputSchema.optional().describe("Where to write the converted document. Omit entirely to receive the bytes inline, base64-encoded, instead."),
155
155
  fonts: z.array(FontInputSchema).optional().describe("Extra font faces to make available to the conversion, for a family the source document does not already embed. Only consulted by a conversion that runs a layout engine (a <format>-to-pdf conversion); every other conversion ignores this option entirely."),
156
- onSubstitutionDiagnostics: z.boolean().optional().describe("When true, additionally report each individual font-substitution event as structured fontSubstitutions (which family/weight/style was requested, what it resolved to instead, and why). Every substitution is always reported as a plain diagnostic in `diagnostics` regardless of this flag -- this only controls whether the fuller, structured event is also collected."),
157
- images: z.record(z.string(), z.string()).optional().describe("A map from a markdown image destination (the part in the parentheses of ![](..)) to its base64-encoded PNG/JPEG bytes, for resolving a markdown source's own non-data: images. Only consulted by a markdown-sourced conversion; every other conversion ignores it. A destination absent from the map degrades to alt text, matching documents.js's own MarkdownImageResolver port -- a caller with no filesystem context has no other way to supply a relative path's bytes.")
156
+ onSubstitutionDiagnostics: z.boolean().optional().describe("When true, additionally report each individual font-substitution event as structured fontSubstitutions (which family/weight/style was requested, what it resolved to instead, and why). Every substitution is always reported as a plain diagnostic in `diagnostics` regardless of this flag — this only controls whether the fuller, structured event is also collected."),
157
+ images: z.record(z.string(), z.string()).optional().describe("A map from a markdown image destination (the part in the parentheses of ![](..)) to its base64-encoded PNG/JPEG bytes, for resolving a markdown source's own non-data: images. Only consulted by a markdown-sourced conversion; every other conversion ignores it. A destination absent from the map degrades to alt text, matching documents.js's own MarkdownImageResolver port — a caller with no filesystem context has no other way to supply a relative path's bytes.")
158
158
  });
159
159
  const ConvertDocumentOutputSchema = z.object({
160
160
  targetFormat: DocumentFormatSchema,
@@ -171,7 +171,7 @@ const converter = createLocalDocumentConverter();
171
171
  const convertDocumentOperation = defineOperation({
172
172
  name: "convert_document",
173
173
  title: "Convert document",
174
- description: "Converts a document from one supported format to another via documents.js's DocumentConverter port -- docx, pptx, xlsx, odt, odp, ods, odg, odf, markdown, rtf, wpd, doc, xls, ppt, epub, csv, svg, and pdf. Not every (source, targetFormat) pair is supported directly (odf, for instance, only ever converts to pdf); call list_document_conversions first to see which pairs actually are.",
174
+ description: "Converts a document from one supported format to another via documents.js's DocumentConverter port — docx, pptx, xlsx, odt, odp, ods, odg, odf, markdown, rtf, wpd, doc, xls, ppt, epub, csv, svg, and pdf. Not every (source, targetFormat) pair is supported directly (odf, for instance, only ever converts to pdf); call list_document_conversions first to see which pairs actually are.",
175
175
  inputSchema: ConvertDocumentInputSchema,
176
176
  outputSchema: ConvertDocumentOutputSchema,
177
177
  async run({ source, targetFormat, output, fonts, onSubstitutionDiagnostics, images }, context) {
@@ -212,7 +212,7 @@ const convertDocumentOperation = defineOperation({
212
212
  const listDocumentConversionsOperation = defineOperation({
213
213
  name: "list_document_conversions",
214
214
  title: "List document conversions",
215
- description: "Lists every (source, target) format pair convert_document actually supports, straight from documents.js's own DocumentConverter port -- the definitive source of truth for what convert_document will and will not accept as a (source, targetFormat) combination.",
215
+ description: "Lists every (source, target) format pair convert_document actually supports, straight from documents.js's own DocumentConverter port — the definitive source of truth for what convert_document will and will not accept as a (source, targetFormat) combination.",
216
216
  inputSchema: ListDocumentConversionsInputSchema,
217
217
  outputSchema: ListDocumentConversionsOutputSchema,
218
218
  run() {
@@ -267,7 +267,7 @@ const ComputeFormulaOutputSchema = z.object({
267
267
  const computeFormulaOperation = defineOperation({
268
268
  name: "compute_formula",
269
269
  title: "Compute document formulas",
270
- description: "Reads every formula a document embeds (docx/odt/markdown paragraphs and pptx/odp slide shapes; a table cell's own blocks too -- docx's own reader recovers a real equation nested inside a table cell; structurally, the walk also covers a drawing page's own shape flow, though no writer in the family populates a formula there today; a spreadsheet's own cell-anchored formula objects; a standalone .odf formula document; and a formula nested inside another embedded object at any depth, e.g. a formula embedded in a drawing embedded in a spreadsheet) and evaluates each through document-compute.js's units-typed evaluate() -- a caller's way to check whether a document's stated arithmetic actually checks out. A formula referencing a symbol (e.g. F = m * a) needs that symbol's value supplied via bindings, keyed by the document's own symbol-table id (see the document's symbolTable.symbols[].id, or a returned formula's own latex to identify which symbol is which); a formula with no free symbols (units and numeric literals only) evaluates with no bindings at all. Each formula reports its own outcome independently -- 'evaluated' with the result, 'no-content' when the formula was never lowered to a computable MathExpression (the common case for a spreadsheet's own embedded formula object, or any format other than markdown, none of which yet stores the semantic layer on disk), or 'error' naming which document-compute.js error it hit (e.g. UnboundSymbolError) -- so one formula needing more bindings never blocks the others. Each entry's own `locate` field is the reliable way to tell two formulas in the same document apart -- a structural path guaranteed unique per formula -- unlike `sourcePath`, which several formats leave undefined or stamp with the identical constant across sibling formulas (markdown's own display-math lowering among them).",
270
+ description: "Reads every formula a document embeds (docx/odt/markdown paragraphs and pptx/odp slide shapes; a table cell's own blocks too — docx's own reader recovers a real equation nested inside a table cell; structurally, the walk also covers a drawing page's own shape flow, though no writer in the family populates a formula there today; a spreadsheet's own cell-anchored formula objects; a standalone .odf formula document; and a formula nested inside another embedded object at any depth, e.g. a formula embedded in a drawing embedded in a spreadsheet) and evaluates each through document-compute.js's units-typed evaluate() — a caller's way to check whether a document's stated arithmetic actually checks out. A formula referencing a symbol (e.g. F = m * a) needs that symbol's value supplied via bindings, keyed by the document's own symbol-table id (see the document's symbolTable.symbols[].id, or a returned formula's own latex to identify which symbol is which); a formula with no free symbols (units and numeric literals only) evaluates with no bindings at all. Each formula reports its own outcome independently — 'evaluated' with the result, 'no-content' when the formula was never lowered to a computable MathExpression (the common case for a spreadsheet's own embedded formula object, or any format other than markdown, none of which yet stores the semantic layer on disk), or 'error' naming which document-compute.js error it hit (e.g. UnboundSymbolError) — so one formula needing more bindings never blocks the others. Each entry's own `locate` field is the reliable way to tell two formulas in the same document apart — a structural path guaranteed unique per formula — unlike `sourcePath`, which several formats leave undefined or stamp with the identical constant across sibling formulas (markdown's own display-math lowering among them).",
271
271
  inputSchema: z.object({
272
272
  source: DocumentInputSchema.describe("The document to read formulas from."),
273
273
  bindings: FormulaBindingsSchema.optional().describe("Known values for symbols the document's formulas reference, keyed by symbol-table id (a Quantity { kind: 'quantity', magnitude, dimension } or an Interval { kind: 'interval', min, max, dimension } per symbol). Omit for a document whose formulas are fully closed (units and numeric literals only, no 'sym' nodes).")
@@ -298,7 +298,7 @@ const computeFormulaOperation = defineOperation({
298
298
  const docxExtrasOperation = defineOperationWithoutOutputSchema({
299
299
  name: "docx_extras",
300
300
  title: "Docx extras",
301
- description: "Reads a docx's own comments, footnotes, header/footer parts, and numbering definitions -- data documents.js's ContentDocument pivot cannot carry, so ordinary document-reading operations never see it. Returns the real DocxExtras object (comments/footnotes/headerFooterParts/sectionHeaderFooters/numbering) as structured data.",
301
+ description: "Reads a docx's own comments, footnotes, header/footer parts, and numbering definitions — data documents.js's ContentDocument pivot cannot carry, so ordinary document-reading operations never see it. Returns the real DocxExtras object (comments/footnotes/headerFooterParts/sectionHeaderFooters/numbering) as structured data.",
302
302
  inputSchema: z.object({ source: DocumentInputSchema.describe("The docx document to read.") }),
303
303
  async run({ source }) {
304
304
  const { bytes } = await resolveDocumentInput(source);
@@ -328,7 +328,7 @@ const RunSchema = z.object({
328
328
  sizePt: z.number().positive().optional().describe("docx/odt only."),
329
329
  colorHex: z.string().regex(/^#?[0-9a-fA-F]{6}$/).optional().describe("6-digit hex colour (e.g. 'ff0000' or '#ff0000'). docx/odt only."),
330
330
  hyperlink: z.string().optional().describe("markdown only."),
331
- code: z.boolean().optional().describe("markdown only -- renders the run as an inline code span.")
331
+ code: z.boolean().optional().describe("markdown only — renders the run as an inline code span.")
332
332
  });
333
333
  const ParagraphSchema = z.object({
334
334
  text: z.string().optional().describe("Plain paragraph text, as a single run. Omit and use `runs` instead for mixed formatting within one paragraph."),
@@ -347,7 +347,7 @@ function unsupportedFieldNames(input, unsupported) {
347
347
  }
348
348
  function rejectUnsupportedFields(input, unsupported, formatLabel, context) {
349
349
  const present = unsupportedFieldNames(input, unsupported);
350
- if (present.length > 0) throw new Error(`${context} does not support ${present.join(", ")} for ${formatLabel} -- remove ${present.length === 1 ? "it" : "them"} or target docx/odt instead.`);
350
+ if (present.length > 0) throw new Error(`${context} does not support ${present.join(", ")} for ${formatLabel} — remove ${present.length === 1 ? "it" : "them"} or target docx/odt instead.`);
351
351
  }
352
352
  const DOCX_ODT_UNSUPPORTED_RUN_FIELDS = ["hyperlink", "code"];
353
353
  const MARKDOWN_UNSUPPORTED_RUN_FIELDS = [
@@ -424,7 +424,7 @@ const ResolvedDocumentOutputSchema$2 = z.union([z.object({
424
424
  const documentCreateOperation = defineOperation({
425
425
  name: "document_create",
426
426
  title: "Create a blank document",
427
- description: "Creates a fresh, blank document in the given format via documents.js's own live-view editors (createDocx/createOdt/createMarkdownEditor/...), the same construction document_append_paragraphs's own edit calls build on. Returns an empty document with no content -- follow with document_append_paragraphs (docx/odt/markdown) to add text.",
427
+ description: "Creates a fresh, blank document in the given format via documents.js's own live-view editors (createDocx/createOdt/createMarkdownEditor/...), the same construction document_append_paragraphs's own edit calls build on. Returns an empty document with no content — follow with document_append_paragraphs (docx/odt/markdown) to add text.",
428
428
  inputSchema: z.object({
429
429
  format: WritableFormatSchema.describe("The format of document to create."),
430
430
  output: DocumentOutputSchema.optional().describe("Where to write the created document. Omit entirely to receive the bytes inline, base64-encoded.")
@@ -437,7 +437,7 @@ const documentCreateOperation = defineOperation({
437
437
  const documentAppendParagraphsOperation = defineOperation({
438
438
  name: "document_append_paragraphs",
439
439
  title: "Append paragraphs to a wordprocessing document",
440
- description: "Appends one or more paragraphs -- each optionally built from several independently-formatted runs -- to the end of a docx, odt, or markdown document, through documents.js's own live-view editors (the same DocxBody.appendParagraph/OdtBody.appendParagraph/MarkdownBody.appendParagraph document-cli's own TUI uses). Does not convert format -- the source document's own format and targetFormat must match. docx and odt share an identical field set (underline, fontFamily, sizePt, colorHex, headingLevel, alignment all supported); markdown supports a different, smaller set (hyperlink, code) and rejects the docx/odt-only fields outright rather than silently dropping them. This covers wordprocessing paragraph/run editing only -- slides, sheets, drawings, tables, lists, and images are a separate, larger editing surface not exposed here yet.",
440
+ description: "Appends one or more paragraphs — each optionally built from several independently-formatted runs — to the end of a docx, odt, or markdown document, through documents.js's own live-view editors (the same DocxBody.appendParagraph/OdtBody.appendParagraph/MarkdownBody.appendParagraph document-cli's own TUI uses). Does not convert format — the source document's own format and targetFormat must match. docx and odt share an identical field set (underline, fontFamily, sizePt, colorHex, headingLevel, alignment all supported); markdown supports a different, smaller set (hyperlink, code) and rejects the docx/odt-only fields outright rather than silently dropping them. This covers wordprocessing paragraph/run editing only — slides, sheets, drawings, tables, lists, and images are a separate, larger editing surface not exposed here yet.",
441
441
  inputSchema: z.object({
442
442
  source: DocumentInputSchema.describe("The document to append paragraphs to."),
443
443
  targetFormat: z.enum([
@@ -451,7 +451,7 @@ const documentAppendParagraphsOperation = defineOperation({
451
451
  outputSchema: ResolvedDocumentOutputSchema$2,
452
452
  async run({ source, targetFormat, paragraphs, output }) {
453
453
  const { bytes, format } = await resolveDocumentInput(source);
454
- if (format !== targetFormat) throw new Error(`source document is "${format}", but targetFormat is "${targetFormat}" -- document_append_paragraphs never converts format, so the two must match.`);
454
+ if (format !== targetFormat) throw new Error(`source document is "${format}", but targetFormat is "${targetFormat}" — document_append_paragraphs never converts format, so the two must match.`);
455
455
  let resultBytes;
456
456
  if (targetFormat === "markdown") {
457
457
  const editor = openMarkdown(decodeMarkdownText(bytes));
@@ -507,7 +507,7 @@ const describeFontFileOperation = defineOperation({
507
507
  name: "describe_font_file",
508
508
  title: "Describe font file",
509
509
  description: "Reads a standalone TrueType/OpenType font file (.ttf/.otf) and reports the family/bold/italic triple it declares about itself.",
510
- inputSchema: z.object({ source: FontFileInputSchema.describe("The standalone .ttf/.otf font file to inspect -- not a document.") }),
510
+ inputSchema: z.object({ source: FontFileInputSchema.describe("The standalone .ttf/.otf font file to inspect — not a document.") }),
511
511
  outputSchema: z.object({
512
512
  family: z.string(),
513
513
  bold: z.boolean(),
@@ -535,9 +535,9 @@ async function readSourceBytes(source) {
535
535
  const fromPackageOperation = defineOperation({
536
536
  name: "from_package",
537
537
  title: "Build document from package",
538
- description: "Rebuilds real document bytes in a target format from a DocumentTree previously serialised to JSON (e.g. by a caller's own --dump-package-equivalent step) -- the read side of the DocumentTree round trip a conversion's onDocument callback produces.",
538
+ description: "Rebuilds real document bytes in a target format from a DocumentTree previously serialised to JSON (e.g. by a caller's own --dump-package-equivalent step) — the read side of the DocumentTree round trip a conversion's onDocument callback produces.",
539
539
  inputSchema: z.object({
540
- source: DocumentInputSchema.describe("The DocumentTree JSON to read. 'path' points at a JSON file on disk -- its extension is never used to infer a document format, since the file holds a DocumentTree, not a document. 'bytesBase64' carries the JSON inline; its 'format' field is required by the shared hybrid input shape but unused by this operation."),
540
+ source: DocumentInputSchema.describe("The DocumentTree JSON to read. 'path' points at a JSON file on disk — its extension is never used to infer a document format, since the file holds a DocumentTree, not a document. 'bytesBase64' carries the JSON inline; its 'format' field is required by the shared hybrid input shape but unused by this operation."),
541
541
  targetFormat: DocumentFormatSchema.describe("The document format to build from the DocumentTree."),
542
542
  output: DocumentOutputSchema.optional().describe("Where to write the resulting document. Omit entirely (or omit outputPath within it) to receive the bytes inline instead.")
543
543
  }),
@@ -562,11 +562,11 @@ const fromPackageOperation = defineOperation({
562
562
  try {
563
563
  result = documentFromJson(parsed);
564
564
  } catch (error) {
565
- if (error instanceof UnrecognizedDocumentSchemaError) throw new Error("'source' has no recognised $schema -- only a file carrying a real DocumentTree (e.g. written by a caller's own --dump-package-equivalent step) can be read back by this operation", { cause: error });
565
+ if (error instanceof UnrecognizedDocumentSchemaError) throw new Error("'source' has no recognised $schema — only a file carrying a real DocumentTree (e.g. written by a caller's own --dump-package-equivalent step) can be read back by this operation", { cause: error });
566
566
  if (error instanceof z.ZodError) throw new Error(`'source' failed ${documentSchemaKindOf(parsed)} validation: ${error.message}`, { cause: error });
567
567
  throw error;
568
568
  }
569
- if (result.kind !== "DocumentTree") throw new Error(`'source' is a ${result.kind}, not a DocumentTree -- only a file carrying a real DocumentTree (e.g. written by a caller's own --dump-package-equivalent step) can be read back by this operation`);
569
+ if (result.kind !== "DocumentTree") throw new Error(`'source' is a ${result.kind}, not a DocumentTree — only a file carrying a real DocumentTree (e.g. written by a caller's own --dump-package-equivalent step) can be read back by this operation`);
570
570
  return resolveDocumentOutput(buildDocumentBytes(result.value, targetFormat), output ?? {});
571
571
  }
572
572
  });
@@ -591,10 +591,10 @@ const ResolvedDocumentOutputSchema = z.union([z.object({
591
591
  const metadataWriteOperation = defineOperation({
592
592
  name: "metadata_write",
593
593
  title: "Write document metadata",
594
- description: "Patches a document's own title/author/subject/keywords, leaving every other field and every other flag as-is. Does not convert format -- the source document's own format and targetFormat must match (or both be 'pdf'); odf (a standalone formula document) is rejected outright as either a source or a target, since it has no write path back out at all. Convert the document to a different format first (e.g. with a documents.js conversion operation) if metadata needs to be set on the result of a format change.",
594
+ description: "Patches a document's own title/author/subject/keywords, leaving every other field and every other flag as-is. Does not convert format — the source document's own format and targetFormat must match (or both be 'pdf'); odf (a standalone formula document) is rejected outright as either a source or a target, since it has no write path back out at all. Convert the document to a different format first (e.g. with a documents.js conversion operation) if metadata needs to be set on the result of a format change.",
595
595
  inputSchema: z.object({
596
596
  source: DocumentInputSchema.describe("The document to patch metadata on."),
597
- targetFormat: DocumentFormatSchema.describe("The format to write the patched document back out as -- must match the source document's own format (or both be 'pdf'). metadata_write never converts format."),
597
+ targetFormat: DocumentFormatSchema.describe("The format to write the patched document back out as — must match the source document's own format (or both be 'pdf'). metadata_write never converts format."),
598
598
  output: DocumentOutputSchema.optional().describe("Where to write the patched document. Omit entirely to receive the bytes inline, base64-encoded."),
599
599
  setTitle: z.string().optional().describe("Set the title field. Omit to leave it exactly as the source document already has it."),
600
600
  setAuthor: z.string().optional().describe("Set the author field. Omit to leave it exactly as the source document already has it."),
@@ -614,7 +614,7 @@ const metadataWriteOperation = defineOperation({
614
614
  });
615
615
  //#endregion
616
616
  //#region src/operations/odb.ts
617
- const ODB_SOURCE_DESCRIPTION = ".odb database to read. 'path' points at the .odb file on disk -- its extension is never used to infer a document format, since documents.js deliberately excludes 'odb' from DocumentFormat (an embedded database front end has no single natural target format -- tables, saved queries, and reports are three unrelated output shapes -- see that package's own README). 'bytesBase64' carries the .odb bytes inline; its 'format' field is required by the shared hybrid input shape but unused by every odb operation.";
617
+ const ODB_SOURCE_DESCRIPTION = ".odb database to read. 'path' points at the .odb file on disk — its extension is never used to infer a document format, since documents.js deliberately excludes 'odb' from DocumentFormat (an embedded database front end has no single natural target format — tables, saved queries, and reports are three unrelated output shapes — see that package's own README). 'bytesBase64' carries the .odb bytes inline; its 'format' field is required by the shared hybrid input shape but unused by every odb operation.";
618
618
  async function resolveOdbBytes(source) {
619
619
  if ("path" in source) {
620
620
  const buffer = await readFile(source.path);
@@ -622,11 +622,11 @@ async function resolveOdbBytes(source) {
622
622
  }
623
623
  return base64ToBytes(source.bytesBase64);
624
624
  }
625
- /** Resolves an .odb DocumentInput straight through to a decoded Package (via documents.js's own decodeOdbPackage) -- the shape every read (as opposed to export) odb operation needs. */
625
+ /** Resolves an .odb DocumentInput straight through to a decoded Package (via documents.js's own decodeOdbPackage) — the shape every read (as opposed to export) odb operation needs. */
626
626
  async function resolveOdbPackage(source) {
627
627
  return decodeOdbPackage(await resolveOdbBytes(source));
628
628
  }
629
- /** Resolves a saved query's own SQL text by name against the .odb's own db:queries (read via readOdbInventory) -- odb_query's own equivalent of document-cli's resolveQuerySql, for the case where the caller named a saved query rather than supplying SQL directly. Throws, naming every available query, when the name doesn't resolve. */
629
+ /** Resolves a saved query's own SQL text by name against the .odb's own db:queries (read via readOdbInventory) — odb_query's own equivalent of document-cli's resolveQuerySql, for the case where the caller named a saved query rather than supplying SQL directly. Throws, naming every available query, when the name doesn't resolve. */
630
630
  function resolveSavedQuerySql(pkg, name) {
631
631
  const inventory = readOdbInventory(pkg);
632
632
  const saved = inventory.queries.find((candidate) => candidate.name === name);
@@ -653,7 +653,7 @@ const OdbSourceInputSchema = z.object({ source: DocumentInputSchema.describe(ODB
653
653
  const odbTablesOperation = defineOperationWithoutOutputSchema({
654
654
  name: "odb_tables",
655
655
  title: "List .odb tables",
656
- description: "Lists every table an embedded .odb database declares -- column names, types, and row data -- across every storage tier documents.js supports (HSQLDB TEXT/CACHED/BINARY script formats, Firebird gbak backups).",
656
+ description: "Lists every table an embedded .odb database declares — column names, types, and row data — across every storage tier documents.js supports (HSQLDB TEXT/CACHED/BINARY script formats, Firebird gbak backups).",
657
657
  inputSchema: OdbSourceInputSchema,
658
658
  async run({ source }) {
659
659
  return readOdbTables(await resolveOdbPackage(source));
@@ -680,7 +680,7 @@ const odbReportsOperation = defineOperationWithoutOutputSchema({
680
680
  const odbQueryOperation = defineOperationWithoutOutputSchema({
681
681
  name: "odb_query",
682
682
  title: "Query an .odb database",
683
- description: "Runs a bounded SELECT (with optional JOINs of any kind, table aliases, a derived table in FROM, and IN/EXISTS subqueries) over an embedded .odb database's own extracted tables, given directly as SQL or by naming one of the database's saved queries. No database engine is involved -- the query runs in memory over the same tables odb_tables would return, against a closed grammar (SELECT/FROM/JOIN [INNER|LEFT [OUTER]|RIGHT [OUTER]|FULL [OUTER]|CROSS|NATURAL] ... [ON|USING]/WHERE/GROUP BY/ORDER BY, with an optional [AS] alias on any table, WHERE and ON also accepting [NOT] IN (SELECT ...) and [NOT] EXISTS (SELECT ...) -- no column aliases); an unsupported construct is reported as an error naming it, never silently ignored.",
683
+ description: "Runs a bounded SELECT (with optional JOINs of any kind, table aliases, a derived table in FROM, and IN/EXISTS subqueries) over an embedded .odb database's own extracted tables, given directly as SQL or by naming one of the database's saved queries. No database engine is involved — the query runs in memory over the same tables odb_tables would return, against a closed grammar (SELECT/FROM/JOIN [INNER|LEFT [OUTER]|RIGHT [OUTER]|FULL [OUTER]|CROSS|NATURAL] ... [ON|USING]/WHERE/GROUP BY/ORDER BY, with an optional [AS] alias on any table, WHERE and ON also accepting [NOT] IN (SELECT ...) and [NOT] EXISTS (SELECT ...) — no column aliases); an unsupported construct is reported as an error naming it, never silently ignored.",
684
684
  inputSchema: z.object({
685
685
  source: DocumentInputSchema.describe(ODB_SOURCE_DESCRIPTION),
686
686
  sql: z.string().describe("A literal SELECT statement to run. Mutually exclusive with \"query\".").optional(),
@@ -699,7 +699,7 @@ const odbToCsvOperation = defineOperationWithoutOutputSchema({
699
699
  description: "Extracts exactly one named table from an embedded .odb database as CSV bytes. The table name is required whenever the database declares more than one table.",
700
700
  inputSchema: z.object({
701
701
  source: DocumentInputSchema.describe(ODB_SOURCE_DESCRIPTION),
702
- table: z.string().describe("The table to export -- required when the .odb declares more than one table.").optional(),
702
+ table: z.string().describe("The table to export — required when the .odb declares more than one table.").optional(),
703
703
  output: DocumentOutputSchema.optional().describe("Where to write the resulting CSV. Omit entirely (or omit outputPath within it) to receive the bytes inline instead.")
704
704
  }),
705
705
  async run({ source, table, output }, context) {
@@ -731,11 +731,11 @@ const OdbRenderReportTargetFormatSchema = z.enum([
731
731
  "pdf"
732
732
  ]);
733
733
  const OdbRenderReportInputSchema = z.object({
734
- source: DocumentInputSchema.describe(".odb database to render a report from. 'path' points at the .odb file on disk -- its extension is never used to infer a document format, since documents.js deliberately excludes 'odb' from DocumentFormat (an embedded database front end has no single natural target format -- tables, saved queries, and reports are three unrelated output shapes -- see that package's own README). 'bytesBase64' carries the .odb bytes inline; its 'format' field is required by the shared hybrid input shape but unused by this operation."),
735
- report: z.string().optional().describe("The name of the report to render. Required only when the .odb declares more than one report -- omitting it when exactly one is declared renders that one automatically."),
734
+ source: DocumentInputSchema.describe(".odb database to render a report from. 'path' points at the .odb file on disk — its extension is never used to infer a document format, since documents.js deliberately excludes 'odb' from DocumentFormat (an embedded database front end has no single natural target format — tables, saved queries, and reports are three unrelated output shapes — see that package's own README). 'bytesBase64' carries the .odb bytes inline; its 'format' field is required by the shared hybrid input shape but unused by this operation."),
735
+ report: z.string().optional().describe("The name of the report to render. Required only when the .odb declares more than one report — omitting it when exactly one is declared renders that one automatically."),
736
736
  targetFormat: OdbRenderReportTargetFormatSchema.describe("The format to render the report into."),
737
737
  output: DocumentOutputSchema.optional().describe("Where to write the rendered report. Omit entirely (or omit outputPath within it) to receive the bytes inline instead."),
738
- fonts: z.array(FontInputSchema).optional().describe("Extra font faces to make available, for a family the rendered report's own text otherwise falls back on. Only consulted when targetFormat is 'pdf' -- docx and odt output is genuine editable text with no font-embedding step of its own, so fonts is ignored for those two targets.")
738
+ fonts: z.array(FontInputSchema).optional().describe("Extra font faces to make available, for a family the rendered report's own text otherwise falls back on. Only consulted when targetFormat is 'pdf' — docx and odt output is genuine editable text with no font-embedding step of its own, so fonts is ignored for those two targets.")
739
739
  });
740
740
  const MathDiagnosticSchema = z.object({
741
741
  kind: z.enum(["unsupported-element", "approximated-element"]),
@@ -772,7 +772,7 @@ const OdbRenderReportOutputSchema = z.union([z.object({
772
772
  const odbRenderReportOperation = defineOperation({
773
773
  name: "odb_render_report",
774
774
  title: "Render .odb report",
775
- description: "Resolves one of an .odb database's own reports -- its data-bound command run through the bounded SQL engine, its rpt: formulas evaluated, its bands laid out -- and renders the result to docx, odt, or pdf.",
775
+ description: "Resolves one of an .odb database's own reports — its data-bound command run through the bounded SQL engine, its rpt: formulas evaluated, its bands laid out — and renders the result to docx, odt, or pdf.",
776
776
  inputSchema: OdbRenderReportInputSchema,
777
777
  outputSchema: OdbRenderReportOutputSchema,
778
778
  async run({ source, report, targetFormat, output, fonts }, context) {
@@ -819,7 +819,7 @@ async function resolveOdmMasterBytes(source) {
819
819
  }
820
820
  const OdmChapterInputSchema = z.object({
821
821
  href: z.string().describe("The chapter's own text:section-source href as declared inside the .odm master document (e.g. '../chapter1.odt')."),
822
- source: DocumentInputSchema.describe("The chapter document's own bytes -- always read as odt, regardless of the format this hybrid input declares.")
822
+ source: DocumentInputSchema.describe("The chapter document's own bytes — always read as odt, regardless of the format this hybrid input declares.")
823
823
  });
824
824
  const OdmToPdfInputSchema = z.object({
825
825
  source: OdmMasterSourceSchema.describe("The .odm master document to convert."),
@@ -838,7 +838,7 @@ const OdmToPdfOutputSchema = z.union([z.object({
838
838
  const odmToPdfOperation = defineOperation({
839
839
  name: "odm_to_pdf",
840
840
  title: "Convert ODM master document to PDF",
841
- description: "Converts a .odm (ODF master document) to PDF. A .odm never carries its own chapters' content inline -- every text:section is a bare external reference to a standalone .odt file -- so each chapter the master document declares must resolve through `chapters` (an explicit href -> document override) and/or `chaptersDir` (a directory searched by the href's own basename), checked in that order. A chapter left unresolved by both fails the whole conversion, naming every unresolved href.",
841
+ description: "Converts a .odm (ODF master document) to PDF. A .odm never carries its own chapters' content inline — every text:section is a bare external reference to a standalone .odt file — so each chapter the master document declares must resolve through `chapters` (an explicit href -> document override) and/or `chaptersDir` (a directory searched by the href's own basename), checked in that order. A chapter left unresolved by both fails the whole conversion, naming every unresolved href.",
842
842
  inputSchema: OdmToPdfInputSchema,
843
843
  outputSchema: OdmToPdfOutputSchema,
844
844
  async run({ source, chapters, chaptersDir, output }) {
@@ -879,7 +879,7 @@ function toOutlineJson(children) {
879
879
  const outlineDocumentOperation = defineOperationWithoutOutputSchema({
880
880
  name: "outline_document",
881
881
  title: "Outline document",
882
- description: "Projects a document's table of contents as a structured outline: reads the source's own native DocumentTree directly (documents.js's readNativeDocumentTree -- no bridging conversion, no discarded output bytes) and runs document-outline.js's buildOutline over it. Groups carry { text, level, children } (a heading's, list item's, slide's, sheet's, or page's own label plus nested children); leaves carry { kind, text }.",
882
+ description: "Projects a document's table of contents as a structured outline: reads the source's own native DocumentTree directly (documents.js's readNativeDocumentTree — no bridging conversion, no discarded output bytes) and runs document-outline.js's buildOutline over it. Groups carry { text, level, children } (a heading's, list item's, slide's, sheet's, or page's own label plus nested children); leaves carry { kind, text }.",
883
883
  inputSchema: z.object({ source: DocumentInputSchema.describe("The document to outline.") }),
884
884
  async run({ source }, context) {
885
885
  const signal = context?.signal;
@@ -933,7 +933,7 @@ const pdfInspectOperation = defineOperationWithoutOutputSchema({
933
933
  //#endregion
934
934
  //#region src/registry.ts
935
935
  /**
936
- * Every document operation this package defines, one entry per MCP tool/CLI command/REST route -- "the same registry as the MCP" a consumer that needs to enumerate every operation (rather than importing one by name) reaches for: an MCP server registers each entry as a tool, a REST server adds one route per entry, and a CLI can validate its own parsed flags against an entry's inputSchema before dispatching to its run().
936
+ * Every document operation this package defines, one entry per MCP tool/CLI command/REST route — "the same registry as the MCP" a consumer that needs to enumerate every operation (rather than importing one by name) reaches for: an MCP server registers each entry as a tool, a REST server adds one route per entry, and a CLI can validate its own parsed flags against an entry's inputSchema before dispatching to its run().
937
937
  */
938
938
  const DOCUMENT_OPERATIONS = [
939
939
  convertDocumentOperation,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "document-operations",
3
- "version": "1.1.50",
4
- "description": "The canonical set of document-conversion, editing, inspection, and .odb operations documents.js exposes -- one Zod input/output schema and one transport-agnostic run() function per operation, shared by document-mcp, document-cli, and a REST API server rather than each defining its own copy.",
3
+ "version": "1.1.52",
4
+ "description": "The canonical set of document-conversion, editing, inspection, and .odb operations documents.js exposes — one Zod input/output schema and one transport-agnostic run() function per operation, shared by document-mcp, document-cli, and a REST API server rather than each defining its own copy.",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -82,10 +82,10 @@
82
82
  "license": "MIT",
83
83
  "packageManager": "pnpm@12.4.1+sha512.2e81e399d73fe8390dab25e06aa788ab7a5908248d2f5a370f82b481147a6a7a367bf8048f9a6fdb6460f21a66f0542dedb8b94ca2c8723596741920b1656d4c",
84
84
  "dependencies": {
85
- "document-compute.js": "1.5.52",
86
- "document-outline.js": "3.9.22",
87
- "document-schema.js": "7.14.0",
88
- "documents.js": "14.1.0",
85
+ "document-compute.js": "1.6.0",
86
+ "document-outline.js": "3.10.0",
87
+ "document-schema.js": "7.15.0",
88
+ "documents.js": "14.2.0",
89
89
  "zod": "4.4.3"
90
90
  },
91
91
  "devDependencies": {
@@ -98,7 +98,7 @@
98
98
  "@vitest/coverage-v8": "4.1.11",
99
99
  "eslint": "10.8.1",
100
100
  "husky": "9.1.7",
101
- "odf.js": "11.0.0",
101
+ "odf.js": "11.1.0",
102
102
  "publint": "0.3.22",
103
103
  "semantic-release": "25.0.9",
104
104
  "tsdown": "0.22.14",