office-open 0.11.0 → 0.12.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.
@@ -2,7 +2,6 @@ import { t as DocumentType } from "../schemas-Dupoq6xM.mjs";
2
2
  import { DocumentOptions } from "@office-open/docx";
3
3
  import { PresentationOptions } from "@office-open/pptx";
4
4
  import { WorkbookOptions } from "@office-open/xlsx";
5
-
6
5
  //#region src/ai/error.d.ts
7
6
  declare function formatToolError(type: string, error: unknown): string;
8
7
  //#endregion
@@ -26,7 +25,7 @@ declare const xlsxTool: import("ai").Tool<WorkbookOptions, {
26
25
  declare const schemaLookupTool: import("ai").Tool<SchemaLookupInput, {
27
26
  type: DocumentType;
28
27
  requested: string[];
29
- definitions: unknown;
28
+ typeText: string;
30
29
  error?: undefined;
31
30
  suggestions?: undefined;
32
31
  } | {
@@ -34,7 +33,7 @@ declare const schemaLookupTool: import("ai").Tool<SchemaLookupInput, {
34
33
  requested: string[];
35
34
  error: string;
36
35
  suggestions: readonly string[];
37
- definitions?: undefined;
36
+ typeText?: undefined;
38
37
  }>;
39
38
  declare const officeOpenTools: {
40
39
  readonly "generate-docx": import("ai").Tool<DocumentOptions, {
@@ -52,7 +51,7 @@ declare const officeOpenTools: {
52
51
  readonly "office-open-schema-lookup": import("ai").Tool<SchemaLookupInput, {
53
52
  type: DocumentType;
54
53
  requested: string[];
55
- definitions: unknown;
54
+ typeText: string;
56
55
  error?: undefined;
57
56
  suggestions?: undefined;
58
57
  } | {
@@ -60,7 +59,7 @@ declare const officeOpenTools: {
60
59
  requested: string[];
61
60
  error: string;
62
61
  suggestions: readonly string[];
63
- definitions?: undefined;
62
+ typeText?: undefined;
64
63
  }>;
65
64
  };
66
65
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ai/error.ts","../../src/ai/index.ts"],"mappings":";;;;;;iBAWgB,eAAA,CAAgB,IAAA,UAAc,KAAc;;;UCe3C,iBAAA;EACf,IAAA,EAAM,YAAY;EAClB,WAAA;AAAA;AAAA,cASW,QAAA,eAAQ,IAAA,CAAA,eAAA;;;;cA+BR,QAAA,eAAQ,IAAA,CAAA,mBAAA;;;;cAgCR,QAAA,eAAQ,IAAA,CAAA,eAAA;;;;cA8BR,gBAAA,eAAgB,IAAA,CAAA,iBAAA;;;;;;;;;;;;;cAoDhB,eAAA;EAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/ai/error.ts","../../src/ai/index.ts"],"mappings":";;;;;iBAWgB,gBAAgB,cAAc;;;UCuB7B;EACf,MAAM;EACN;;cAkBW,uBAAQ,KAAA;;;;cA6BR,uBAAQ,KAAA;;;;cA6BR,uBAAQ,KAAA;;;;cAqCR,+BAAgB,KAAA;;;;;;;;;;;;;cAyDhB"}
package/dist/ai/index.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  import { generate } from "../generate.mjs";
2
- import { f as validateDocumentInput, i as sliceDocumentSchema, n as UnknownDefinitionError, t as getSkeletonSchema } from "../schemas-BKyaDGmp.mjs";
2
+ import { a as sliceDocumentSchema, g as validateDocumentInput, n as getSkeletonSchema, r as UnknownDefinitionError, t as renderSliceTypeText } from "../schemas-DhCWsZ8v.mjs";
3
+ import { lintWorkbookFormulas } from "@office-open/xlsx";
4
+ import { PART_REGISTRIES, encodeBase64, unzipSync, validateOpcConsistency } from "@office-open/core";
3
5
  import { jsonSchema, tool } from "ai";
4
6
  //#region src/ai/error.ts
5
7
  /**
@@ -21,63 +23,119 @@ function formatToolError(type, error) {
21
23
  return `${type.toUpperCase()} generation failed: ${msg}`;
22
24
  }
23
25
  //#endregion
26
+ //#region src/ai/verify.ts
27
+ /**
28
+ * Post-generation gate for the AI tools: run the OPC package consistency check
29
+ * on freshly generated bytes before handing them to the model.
30
+ *
31
+ * Fresh output is fully under library control, so ANY issue (error or warn) is
32
+ * a library regression, not an options error — fail fast with the diagnosis
33
+ * instead of shipping a file Word/Excel report as corrupt. Mirrors what
34
+ * scripts/validate.ts runs in CI; O7-style duplicate relationship ids are
35
+ * exactly the class of breakage this catches before a user sees it.
36
+ *
37
+ * @module
38
+ */
39
+ const decoder = new TextDecoder("utf-8", { fatal: false });
40
+ /** OPC-check a fresh package and return it as base64 for the tool result. */
41
+ function generateVerifiedBase64(type, bytes) {
42
+ const files = unzipSync(bytes);
43
+ const entries = /* @__PURE__ */ new Map();
44
+ for (const name of Object.keys(files)) entries.set(name, decoder.decode(files[name]));
45
+ const issues = validateOpcConsistency(entries, PART_REGISTRIES[type]);
46
+ if (issues.length > 0) {
47
+ const lines = issues.map((i) => ` ${i.code} [${i.severity}] ${i.part}: ${i.message}`);
48
+ throw new Error(`Generated ${type} failed the OPC package consistency check — your options are valid, this is an office-open bug; please report it:\n${lines.join("\n")}`);
49
+ }
50
+ return encodeBase64(bytes);
51
+ }
52
+ //#endregion
24
53
  //#region src/ai/index.ts
54
+ /**
55
+ * The generate tools return the file as base64 for the client UI, but the
56
+ * model only needs the outcome — full base64 in the model context would burn
57
+ * thousands of tokens per document.
58
+ */
59
+ function documentGeneratedSummary(output) {
60
+ const kb = Math.ceil(output.base64.length * 3 / 4 / 1024);
61
+ return `Document generated and all validations passed (${output.mimeType}, ${kb} KB).`;
62
+ }
25
63
  const docxTool = tool({
26
- description: "Generate a .docx Word document. The input is the document options directly — must include a 'sections' array. Each section has 'children' (paragraphs, tables, etc.). IMPORTANT: Section children must use wrapper keys: { paragraph: {...} }, { table: {...} }, { toc: {...} }, { textbox: {...} }. Paragraph children must use: { text: '...', bold?: true, italic?: true, size?: number, ... }. The 'text' key is required in run objects. Plain strings are also accepted. Colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. Optional metadata: title, creator, subject, styles, numbering, comments, footnotes, endnotes, background, features. This input schema is a skeleton (top-level shape + child wrapper keys only). Stubs name the definition they stand for before filling paragraph/table/chart/style details, call the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. Invalid structures are rejected with instance-path errors; fix and retry.",
64
+ description: "Generate a .docx Word document. The input is the document options directly — must include a 'sections' array. Conventions: section children are wrapper-key objects ({ paragraph: {...} }, { table: {...} }, …); run objects require a 'text' key (plain strings also accepted); colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. This schema is a skeleton stubs name the definition they stand for. Fetch real fields with the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. Invalid input is rejected with instance-path errors; fix and retry.",
27
65
  inputSchema: jsonSchema(getSkeletonSchema("docx")),
28
66
  execute: async (options) => {
29
67
  try {
68
+ const validated = validateDocumentInput("docx", options);
30
69
  return {
31
- base64: await generate({
70
+ base64: generateVerifiedBase64("docx", await generate({
32
71
  type: "docx",
33
- options: validateDocumentInput("docx", options),
34
- outputType: "base64"
35
- }),
72
+ options: validated,
73
+ outputType: "uint8array"
74
+ })),
36
75
  mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
37
76
  };
38
77
  } catch (error) {
39
78
  throw new Error(formatToolError("docx", error));
40
79
  }
41
- }
80
+ },
81
+ toModelOutput: ({ output }) => ({
82
+ type: "text",
83
+ value: documentGeneratedSummary(output)
84
+ })
42
85
  });
43
86
  const pptxTool = tool({
44
- description: "Generate a .pptx PowerPoint presentation. The input is the presentation options directly — must include a 'slides' array. Each slide has 'children' (shapes, pictures, tables, charts, groups, etc.). Slide children use wrapper keys: { shape: {...} }, { picture: {...} }, { table: {...} }, { chart: {...} }, etc. Shapes use { shape: { x, y, width, height, textBody?, fill?, ... } }. IMPORTANT: Shape positions (x, y, width, height) are in pixels. Colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. Fill can be a hex color string or a fill object: '4472C4' or { type: 'solidFill', color: '4472C4' }. Optional: size ('16:9' or '4:3' or { width, height }), title, creator, masters, show. This input schema is a skeleton (top-level shape + child wrapper keys only). Stubs name the definition they stand for before filling paragraph/table/chart/style details, call the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. Invalid structures are rejected with instance-path errors; fix and retry.",
87
+ description: "Generate a .pptx PowerPoint presentation. The input is the presentation options directly — must include a 'slides' array. Conventions: shape x/y/width/height take UniversalMeasure strings ('2cm', '1in', '96px') or raw EMU numbers (914400 = 1 inch); fills are hex color strings or fill objects ('4472C4' or { type: 'solidFill', color: '4472C4' }); colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. This schema is a skeleton stubs name the definition they stand for. Fetch real fields with the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. Invalid input is rejected with instance-path errors; fix and retry.",
45
88
  inputSchema: jsonSchema(getSkeletonSchema("pptx")),
46
89
  execute: async (options) => {
47
90
  try {
91
+ const validated = validateDocumentInput("pptx", options);
48
92
  return {
49
- base64: await generate({
93
+ base64: generateVerifiedBase64("pptx", await generate({
50
94
  type: "pptx",
51
- options: validateDocumentInput("pptx", options),
52
- outputType: "base64"
53
- }),
95
+ options: validated,
96
+ outputType: "uint8array"
97
+ })),
54
98
  mimeType: "application/vnd.openxmlformats-officedocument.presentationml.presentation"
55
99
  };
56
100
  } catch (error) {
57
101
  throw new Error(formatToolError("pptx", error));
58
102
  }
59
- }
103
+ },
104
+ toModelOutput: ({ output }) => ({
105
+ type: "text",
106
+ value: documentGeneratedSummary(output)
107
+ })
60
108
  });
61
109
  const xlsxTool = tool({
62
- description: "Generate a .xlsx Excel spreadsheet. The input is the workbook options directly — must include a 'worksheets' array. Each worksheet has 'rows' — an array of row objects, each with 'cells'. Cell values: string, number, boolean, null. Use 'style' for formatting. IMPORTANT: Cells can be shorthand values (string, number, boolean) or objects: { value: 'hello', style: { ... } }. Column widths use 'width' as a number. Optional: columns, mergeCells, freezePanes, autoFilter, images, charts, dataValidations, conditionalFormats. This input schema is a skeleton (top-level shape + child wrapper keys only). Stubs name the definition they stand for before filling paragraph/table/chart/style details, call the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. Invalid structures are rejected with instance-path errors; fix and retry.",
110
+ description: "Generate a .xlsx Excel spreadsheet. The input is the workbook options directly — must include a 'worksheets' array. Conventions: cells are shorthand values (string, number, boolean, null) or { value, style } objects; column 'width' is in Excel character units. This schema is a skeleton stubs name the definition they stand for. Fetch real fields with the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. Invalid input is rejected with instance-path errors; fix and retry.",
63
111
  inputSchema: jsonSchema(getSkeletonSchema("xlsx")),
64
112
  execute: async (options) => {
65
113
  try {
114
+ const validated = validateDocumentInput("xlsx", options);
115
+ const formulaIssues = lintWorkbookFormulas(validated);
116
+ if (formulaIssues.length > 0) {
117
+ const lines = formulaIssues.map((i) => ` ${i.location}: ${i.message} — formula "${i.formula}"`);
118
+ throw new Error(`Invalid xlsx formulas:\n${lines.join("\n")}\nFix the formula or add the missing worksheet.`);
119
+ }
66
120
  return {
67
- base64: await generate({
121
+ base64: generateVerifiedBase64("xlsx", await generate({
68
122
  type: "xlsx",
69
- options: validateDocumentInput("xlsx", options),
70
- outputType: "base64"
71
- }),
123
+ options: validated,
124
+ outputType: "uint8array"
125
+ })),
72
126
  mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
73
127
  };
74
128
  } catch (error) {
75
129
  throw new Error(formatToolError("xlsx", error));
76
130
  }
77
- }
131
+ },
132
+ toModelOutput: ({ output }) => ({
133
+ type: "text",
134
+ value: documentGeneratedSummary(output)
135
+ })
78
136
  });
79
137
  const schemaLookupTool = tool({
80
- description: "Fetch the precise JSON Schema (draft-07) for office-open option definitions on demand. Use it before filling complex objects into the generate tools: the generate input schemas are skeletons whose stubs name the definition to look up here. Valid names come from the skeleton stubs, or list indexed entries with `npx office-open schema index <type>` (all names with --all). Returns the requested definitions plus their dependency closure; cataloged domains not requested stay as expandable stubs, so request each domain root you need (e.g. ['ParagraphOptions', 'RunOptions', 'TableOptions']).",
138
+ description: "Fetch the precise type definitions for office-open option fields on demand. Use it before filling complex objects into the generate tools: the generate input schemas are skeletons whose stubs name the definition to look up here. Valid names come from the skeleton stubs, or list indexed entries with `npx office-open schema index <type>` (all names with --all). Returns the requested definitions plus their dependency closure as type-definition text (field types, \"a\" | \"b\" value enums, optional markers, one-line comments); cataloged domains not requested stay as stubs, so request each domain root you need (e.g. ['ParagraphOptions', 'RunOptions', 'TableOptions']).",
81
139
  inputSchema: jsonSchema({
82
140
  type: "object",
83
141
  additionalProperties: false,
@@ -103,10 +161,11 @@ const schemaLookupTool = tool({
103
161
  }),
104
162
  execute: async ({ type, definitions }) => {
105
163
  try {
164
+ const slice = sliceDocumentSchema(type, definitions);
106
165
  return {
107
166
  type,
108
167
  requested: definitions,
109
- definitions: sliceDocumentSchema(type, definitions).definitions
168
+ typeText: renderSliceTypeText(type, definitions, slice)
110
169
  };
111
170
  } catch (error) {
112
171
  if (error instanceof UnknownDefinitionError) return {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/ai/error.ts","../../src/ai/index.ts"],"sourcesContent":["/**\n * Format tool execution errors into LLM-friendly messages.\n *\n * When a tool's `execute` function throws, the AI SDK captures the error as a\n * `tool-error` content part and sends it back to the LLM for self-correction.\n * This module rewrites internal runtime errors into messages the LLM can\n * understand and act on.\n *\n * @module\n */\n\nexport function formatToolError(type: string, error: unknown): string {\n const msg = error instanceof Error ? error.message : String(error);\n\n // ── DOCX: paragraph child errors ──\n\n if (msg.includes(\"Unsupported paragraph child type:\")) {\n const keys = msg.split(\": \").pop() ?? \"\";\n return (\n `Invalid paragraph child \"${keys}\". ` +\n `Paragraph children must use a wrapper key: ` +\n `{ paragraph: { children: [{ text: \"...\", bold?: true }] } }, ` +\n `{ table: { rows: [...] } }, { picture: { ... } }, ` +\n `{ toc: { ... } }, { textbox: { ... } }, ` +\n `{ pageBreak: true }, { columnBreak: true }, etc. ` +\n `Do not use raw property names like { bold: ... } as paragraph children.`\n );\n }\n\n if (msg.includes(\"Unsupported run child type:\")) {\n const keys = msg.split(\": \").pop() ?? \"\";\n return (\n `Invalid run child \"${keys}\". ` +\n `Run children must be text objects: { text: \"...\", bold?: true, italic?: true, size?: number, color?: \"RRGGBB\", ... }. ` +\n `The \"text\" key is required. Plain strings are also accepted as children. ` +\n `Do not use bare property objects like { bold: true } without a \"text\" key.`\n );\n }\n\n if (msg.includes(\"Unknown section child type\")) {\n return (\n `Unknown section child. ` +\n `Section children must use a wrapper key: ` +\n `{ paragraph: { ... } }, { table: { ... } }, ` +\n `{ toc: { ... } }, { textbox: { ... } }, { pageBreak: true }, ` +\n `{ sdt: { ... } }, { altChunk: { ... } }, etc.`\n );\n }\n\n // ── General: iterable errors ──\n\n if (msg.includes(\"not iterable\")) {\n const field = type === \"docx\" ? \"sections\" : type === \"pptx\" ? \"slides\" : \"worksheets\";\n return `\"${field}\" must be an array. Received a non-iterable value.`;\n }\n\n // ── Fallback ──\n\n return `${type.toUpperCase()} generation failed: ${msg}`;\n}\n","/**\n * Vercel AI SDK tools for generating Office documents.\n *\n * The generate tools use skeleton input schemas (top-level shape + wrapper\n * keys only) instead of the full format schema (~675 KB for docx) — no\n * provider accepts that in a tool definition. Precise field schemas are\n * fetched on demand through the office-open-schema-lookup tool, and the\n * authoritative ajv validation runs inside execute with instancePath-\n * qualified errors the model can iterate on.\n *\n * @module\n */\nimport type { DocumentOptions } from \"@office-open/docx\";\nimport type { PresentationOptions } from \"@office-open/pptx\";\nimport type { WorkbookOptions } from \"@office-open/xlsx\";\nimport { jsonSchema, tool } from \"ai\";\n\nexport { formatToolError } from \"./error\";\n\nimport { generate } from \"../generate\";\nimport { getSkeletonSchema, sliceDocumentSchema, validateDocumentInput } from \"../schemas\";\nimport type { DocumentType } from \"../schemas/schemas\";\nimport { UnknownDefinitionError } from \"../schemas/slice\";\nimport { formatToolError } from \"./error\";\n\n/** Input accepted by the office-open-schema-lookup tool. */\nexport interface SchemaLookupInput {\n type: DocumentType;\n definitions: string[];\n}\n\nconst SKELETON_GUIDANCE =\n \"This input schema is a skeleton (top-level shape + child wrapper keys only). \" +\n \"Stubs name the definition they stand for — before filling paragraph/table/chart/style details, \" +\n \"call the office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. \" +\n \"Invalid structures are rejected with instance-path errors; fix and retry.\";\n\nexport const docxTool = tool({\n description:\n \"Generate a .docx Word document. \" +\n \"The input is the document options directly — must include a 'sections' array. \" +\n \"Each section has 'children' (paragraphs, tables, etc.). \" +\n \"IMPORTANT: \" +\n \"Section children must use wrapper keys: { paragraph: {...} }, { table: {...} }, { toc: {...} }, { textbox: {...} }. \" +\n \"Paragraph children must use: { text: '...', bold?: true, italic?: true, size?: number, ... }. \" +\n \"The 'text' key is required in run objects. Plain strings are also accepted. \" +\n \"Colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. \" +\n \"Optional metadata: title, creator, subject, styles, numbering, comments, footnotes, endnotes, background, features. \" +\n SKELETON_GUIDANCE,\n inputSchema: jsonSchema<DocumentOptions>(getSkeletonSchema(\"docx\")),\n execute: async (options) => {\n try {\n const validated = validateDocumentInput(\"docx\", options);\n const base64 = (await generate({\n type: \"docx\",\n options: validated as unknown as DocumentOptions,\n outputType: \"base64\",\n })) as string;\n return {\n base64,\n mimeType: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n };\n } catch (error) {\n throw new Error(formatToolError(\"docx\", error));\n }\n },\n});\n\nexport const pptxTool = tool({\n description:\n \"Generate a .pptx PowerPoint presentation. \" +\n \"The input is the presentation options directly — must include a 'slides' array. \" +\n \"Each slide has 'children' (shapes, pictures, tables, charts, groups, etc.). \" +\n \"Slide children use wrapper keys: { shape: {...} }, { picture: {...} }, { table: {...} }, { chart: {...} }, etc. \" +\n \"Shapes use { shape: { x, y, width, height, textBody?, fill?, ... } }. \" +\n \"IMPORTANT: \" +\n \"Shape positions (x, y, width, height) are in pixels. \" +\n \"Colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. \" +\n \"Fill can be a hex color string or a fill object: '4472C4' or { type: 'solidFill', color: '4472C4' }. \" +\n \"Optional: size ('16:9' or '4:3' or { width, height }), title, creator, masters, show. \" +\n SKELETON_GUIDANCE,\n inputSchema: jsonSchema<PresentationOptions>(getSkeletonSchema(\"pptx\")),\n execute: async (options) => {\n try {\n const validated = validateDocumentInput(\"pptx\", options);\n const base64 = (await generate({\n type: \"pptx\",\n options: validated as unknown as PresentationOptions,\n outputType: \"base64\",\n })) as string;\n return {\n base64,\n mimeType: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n };\n } catch (error) {\n throw new Error(formatToolError(\"pptx\", error));\n }\n },\n});\n\nexport const xlsxTool = tool({\n description:\n \"Generate a .xlsx Excel spreadsheet. \" +\n \"The input is the workbook options directly — must include a 'worksheets' array. \" +\n \"Each worksheet has 'rows' — an array of row objects, each with 'cells'. \" +\n \"Cell values: string, number, boolean, null. Use 'style' for formatting. \" +\n \"IMPORTANT: \" +\n \"Cells can be shorthand values (string, number, boolean) or objects: { value: 'hello', style: { ... } }. \" +\n \"Column widths use 'width' as a number. \" +\n \"Optional: columns, mergeCells, freezePanes, autoFilter, images, charts, dataValidations, conditionalFormats. \" +\n SKELETON_GUIDANCE,\n inputSchema: jsonSchema<WorkbookOptions>(getSkeletonSchema(\"xlsx\")),\n execute: async (options) => {\n try {\n const validated = validateDocumentInput(\"xlsx\", options);\n const base64 = (await generate({\n type: \"xlsx\",\n options: validated as unknown as WorkbookOptions,\n outputType: \"base64\",\n })) as string;\n return {\n base64,\n mimeType: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n };\n } catch (error) {\n throw new Error(formatToolError(\"xlsx\", error));\n }\n },\n});\n\nexport const schemaLookupTool = tool({\n description:\n \"Fetch the precise JSON Schema (draft-07) for office-open option definitions on demand. \" +\n \"Use it before filling complex objects into the generate tools: the generate input schemas are \" +\n \"skeletons whose stubs name the definition to look up here. \" +\n \"Valid names come from the skeleton stubs, or list indexed entries with \" +\n \"`npx office-open schema index <type>` (all names with --all). \" +\n \"Returns the requested definitions plus their dependency closure; cataloged domains not \" +\n \"requested stay as expandable stubs, so request each domain root you need (e.g. \" +\n \"['ParagraphOptions', 'RunOptions', 'TableOptions']).\",\n inputSchema: jsonSchema<SchemaLookupInput>({\n type: \"object\",\n additionalProperties: false,\n required: [\"type\", \"definitions\"],\n properties: {\n type: {\n type: \"string\",\n enum: [\"docx\", \"pptx\", \"xlsx\"],\n description: \"Document format whose schema to slice\",\n },\n definitions: {\n type: \"array\",\n items: { type: \"string\" },\n minItems: 1,\n maxItems: 8,\n description:\n \"Definition names (TS type names, e.g. ParagraphOptions, SlideOptions, StyleOptions). \" +\n \"At most 8 per call.\",\n },\n },\n }),\n execute: async ({ type, definitions }) => {\n try {\n const slice = sliceDocumentSchema(type, definitions);\n return { type, requested: definitions, definitions: slice.definitions };\n } catch (error) {\n if (error instanceof UnknownDefinitionError) {\n // Data, not a throw: lets the model self-correct from the suggestions.\n return {\n type,\n requested: definitions,\n error:\n `${error.message}. Closest: ${error.suggestions.join(\", \") || \"none\"}. ` +\n `List indexed entries with: npx office-open schema index ${type}`,\n suggestions: error.suggestions,\n };\n }\n throw error;\n }\n },\n});\n\nexport const officeOpenTools = {\n \"generate-docx\": docxTool,\n \"generate-pptx\": pptxTool,\n \"generate-xlsx\": xlsxTool,\n \"office-open-schema-lookup\": schemaLookupTool,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;AAWA,SAAgB,gBAAgB,MAAc,OAAwB;CACpE,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CAIjE,IAAI,IAAI,SAAS,mCAAmC,GAElD,OACE,4BAFW,IAAI,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,GAEH;CAUrC,IAAI,IAAI,SAAS,6BAA6B,GAE5C,OACE,sBAFW,IAAI,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,GAET;CAO/B,IAAI,IAAI,SAAS,4BAA4B,GAC3C,OACE;CAUJ,IAAI,IAAI,SAAS,cAAc,GAE7B,OAAO,IADO,SAAS,SAAS,aAAa,SAAS,SAAS,WAAW,aACzD;CAKnB,OAAO,GAAG,KAAK,YAAY,EAAE,sBAAsB;AACrD;;;ACtBA,MAAa,WAAW,KAAK;CAC3B,aACE;CAUF,aAAa,WAA4B,kBAAkB,MAAM,CAAC;CAClE,SAAS,OAAO,YAAY;EAC1B,IAAI;GAOF,OAAO;IACL,QAAA,MANoB,SAAS;KAC7B,MAAM;KACN,SAHgB,sBAAsB,QAAQ,OAG7B;KACjB,YAAY;IACd,CAAC;IAGC,UAAU;GACZ;EACF,SAAS,OAAO;GACd,MAAM,IAAI,MAAM,gBAAgB,QAAQ,KAAK,CAAC;EAChD;CACF;AACF,CAAC;AAED,MAAa,WAAW,KAAK;CAC3B,aACE;CAWF,aAAa,WAAgC,kBAAkB,MAAM,CAAC;CACtE,SAAS,OAAO,YAAY;EAC1B,IAAI;GAOF,OAAO;IACL,QAAA,MANoB,SAAS;KAC7B,MAAM;KACN,SAHgB,sBAAsB,QAAQ,OAG7B;KACjB,YAAY;IACd,CAAC;IAGC,UAAU;GACZ;EACF,SAAS,OAAO;GACd,MAAM,IAAI,MAAM,gBAAgB,QAAQ,KAAK,CAAC;EAChD;CACF;AACF,CAAC;AAED,MAAa,WAAW,KAAK;CAC3B,aACE;CASF,aAAa,WAA4B,kBAAkB,MAAM,CAAC;CAClE,SAAS,OAAO,YAAY;EAC1B,IAAI;GAOF,OAAO;IACL,QAAA,MANoB,SAAS;KAC7B,MAAM;KACN,SAHgB,sBAAsB,QAAQ,OAG7B;KACjB,YAAY;IACd,CAAC;IAGC,UAAU;GACZ;EACF,SAAS,OAAO;GACd,MAAM,IAAI,MAAM,gBAAgB,QAAQ,KAAK,CAAC;EAChD;CACF;AACF,CAAC;AAED,MAAa,mBAAmB,KAAK;CACnC,aACE;CAQF,aAAa,WAA8B;EACzC,MAAM;EACN,sBAAsB;EACtB,UAAU,CAAC,QAAQ,aAAa;EAChC,YAAY;GACV,MAAM;IACJ,MAAM;IACN,MAAM;KAAC;KAAQ;KAAQ;IAAM;IAC7B,aAAa;GACf;GACA,aAAa;IACX,MAAM;IACN,OAAO,EAAE,MAAM,SAAS;IACxB,UAAU;IACV,UAAU;IACV,aACE;GAEJ;EACF;CACF,CAAC;CACD,SAAS,OAAO,EAAE,MAAM,kBAAkB;EACxC,IAAI;GAEF,OAAO;IAAE;IAAM,WAAW;IAAa,aADzB,oBAAoB,MAAM,WACgB,CAAC,CAAC;GAAY;EACxE,SAAS,OAAO;GACd,IAAI,iBAAiB,wBAEnB,OAAO;IACL;IACA,WAAW;IACX,OACE,GAAG,MAAM,QAAQ,aAAa,MAAM,YAAY,KAAK,IAAI,KAAK,OAAO,4DACV;IAC7D,aAAa,MAAM;GACrB;GAEF,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,kBAAkB;CAC7B,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,6BAA6B;AAC/B"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/ai/error.ts","../../src/ai/verify.ts","../../src/ai/index.ts"],"sourcesContent":["/**\n * Format tool execution errors into LLM-friendly messages.\n *\n * When a tool's `execute` function throws, the AI SDK captures the error as a\n * `tool-error` content part and sends it back to the LLM for self-correction.\n * This module rewrites internal runtime errors into messages the LLM can\n * understand and act on.\n *\n * @module\n */\n\nexport function formatToolError(type: string, error: unknown): string {\n const msg = error instanceof Error ? error.message : String(error);\n\n // ── DOCX: paragraph child errors ──\n\n if (msg.includes(\"Unsupported paragraph child type:\")) {\n const keys = msg.split(\": \").pop() ?? \"\";\n return (\n `Invalid paragraph child \"${keys}\". ` +\n `Paragraph children must use a wrapper key: ` +\n `{ paragraph: { children: [{ text: \"...\", bold?: true }] } }, ` +\n `{ table: { rows: [...] } }, { picture: { ... } }, ` +\n `{ toc: { ... } }, { textbox: { ... } }, ` +\n `{ pageBreak: true }, { columnBreak: true }, etc. ` +\n `Do not use raw property names like { bold: ... } as paragraph children.`\n );\n }\n\n if (msg.includes(\"Unsupported run child type:\")) {\n const keys = msg.split(\": \").pop() ?? \"\";\n return (\n `Invalid run child \"${keys}\". ` +\n `Run children must be text objects: { text: \"...\", bold?: true, italic?: true, size?: number, color?: \"RRGGBB\", ... }. ` +\n `The \"text\" key is required. Plain strings are also accepted as children. ` +\n `Do not use bare property objects like { bold: true } without a \"text\" key.`\n );\n }\n\n if (msg.includes(\"Unknown section child type\")) {\n return (\n `Unknown section child. ` +\n `Section children must use a wrapper key: ` +\n `{ paragraph: { ... } }, { table: { ... } }, ` +\n `{ toc: { ... } }, { textbox: { ... } }, { pageBreak: true }, ` +\n `{ sdt: { ... } }, { altChunk: { ... } }, etc.`\n );\n }\n\n // ── General: iterable errors ──\n\n if (msg.includes(\"not iterable\")) {\n const field = type === \"docx\" ? \"sections\" : type === \"pptx\" ? \"slides\" : \"worksheets\";\n return `\"${field}\" must be an array. Received a non-iterable value.`;\n }\n\n // ── Fallback ──\n\n return `${type.toUpperCase()} generation failed: ${msg}`;\n}\n","/**\n * Post-generation gate for the AI tools: run the OPC package consistency check\n * on freshly generated bytes before handing them to the model.\n *\n * Fresh output is fully under library control, so ANY issue (error or warn) is\n * a library regression, not an options error — fail fast with the diagnosis\n * instead of shipping a file Word/Excel report as corrupt. Mirrors what\n * scripts/validate.ts runs in CI; O7-style duplicate relationship ids are\n * exactly the class of breakage this catches before a user sees it.\n *\n * @module\n */\nimport {\n encodeBase64,\n PART_REGISTRIES,\n unzipSync,\n validateOpcConsistency,\n} from \"@office-open/core\";\n\nimport type { GenerateType } from \"../generate\";\n\n// Binary parts (media, fonts) decode to replacement chars — the check only\n// reads part presence and paths, never their content.\nconst decoder = new TextDecoder(\"utf-8\", { fatal: false });\n\n/** OPC-check a fresh package and return it as base64 for the tool result. */\nexport function generateVerifiedBase64(type: GenerateType, bytes: Uint8Array): string {\n const files = unzipSync(bytes);\n const entries = new Map<string, string>();\n for (const name of Object.keys(files)) {\n entries.set(name, decoder.decode(files[name]));\n }\n const issues = validateOpcConsistency(entries, PART_REGISTRIES[type]!);\n if (issues.length > 0) {\n const lines = issues.map((i) => ` ${i.code} [${i.severity}] ${i.part}: ${i.message}`);\n throw new Error(\n `Generated ${type} failed the OPC package consistency check — your options are valid, ` +\n `this is an office-open bug; please report it:\\n${lines.join(\"\\n\")}`,\n );\n }\n return encodeBase64(bytes);\n}\n","/**\n * Vercel AI SDK tools for generating Office documents.\n *\n * The generate tools use skeleton input schemas (top-level shape + wrapper\n * keys only) instead of the full format schema (~675 KB for docx) — no\n * provider accepts that in a tool definition. Precise field schemas are\n * fetched on demand through the office-open-schema-lookup tool, and the\n * authoritative ajv validation runs inside execute with instancePath-\n * qualified errors the model can iterate on.\n *\n * @module\n */\nimport type { DocumentOptions } from \"@office-open/docx\";\nimport type { PresentationOptions } from \"@office-open/pptx\";\nimport type { WorkbookOptions } from \"@office-open/xlsx\";\nimport { jsonSchema, tool } from \"ai\";\n\nexport { formatToolError } from \"./error\";\n\nimport { lintWorkbookFormulas } from \"@office-open/xlsx\";\n\nimport { generate } from \"../generate\";\nimport {\n getSkeletonSchema,\n renderSliceTypeText,\n sliceDocumentSchema,\n validateDocumentInput,\n} from \"../schemas\";\nimport type { DocumentType } from \"../schemas/schemas\";\nimport { UnknownDefinitionError } from \"../schemas/slice\";\nimport { formatToolError } from \"./error\";\nimport { generateVerifiedBase64 } from \"./verify\";\n\n/** Input accepted by the office-open-schema-lookup tool. */\nexport interface SchemaLookupInput {\n type: DocumentType;\n definitions: string[];\n}\n\nconst SKELETON_GUIDANCE =\n \"This schema is a skeleton — stubs name the definition they stand for. Fetch real fields with the \" +\n \"office-open-schema-lookup tool, e.g. { type: 'docx', definitions: ['ParagraphOptions', 'RunOptions'] }. \" +\n \"Invalid input is rejected with instance-path errors; fix and retry.\";\n\n/**\n * The generate tools return the file as base64 for the client UI, but the\n * model only needs the outcome — full base64 in the model context would burn\n * thousands of tokens per document.\n */\nfunction documentGeneratedSummary(output: { base64: string; mimeType: string }): string {\n const kb = Math.ceil((output.base64.length * 3) / 4 / 1024);\n return `Document generated and all validations passed (${output.mimeType}, ${kb} KB).`;\n}\n\nexport const docxTool = tool({\n description:\n \"Generate a .docx Word document. \" +\n \"The input is the document options directly — must include a 'sections' array. \" +\n \"Conventions: \" +\n \"section children are wrapper-key objects ({ paragraph: {...} }, { table: {...} }, …); \" +\n \"run objects require a 'text' key (plain strings also accepted); \" +\n \"colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. \" +\n SKELETON_GUIDANCE,\n inputSchema: jsonSchema<DocumentOptions>(getSkeletonSchema(\"docx\")),\n execute: async (options) => {\n try {\n const validated = validateDocumentInput(\"docx\", options);\n const bytes = (await generate({\n type: \"docx\",\n options: validated as unknown as DocumentOptions,\n outputType: \"uint8array\",\n })) as Uint8Array;\n return {\n base64: generateVerifiedBase64(\"docx\", bytes),\n mimeType: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n };\n } catch (error) {\n throw new Error(formatToolError(\"docx\", error));\n }\n },\n toModelOutput: ({ output }) => ({ type: \"text\", value: documentGeneratedSummary(output) }),\n});\n\nexport const pptxTool = tool({\n description:\n \"Generate a .pptx PowerPoint presentation. \" +\n \"The input is the presentation options directly — must include a 'slides' array. \" +\n \"Conventions: \" +\n \"shape x/y/width/height take UniversalMeasure strings ('2cm', '1in', '96px') or raw EMU numbers (914400 = 1 inch); \" +\n \"fills are hex color strings or fill objects ('4472C4' or { type: 'solidFill', color: '4472C4' }); \" +\n \"colors are hex WITHOUT '#': 'FF0000', not '#FF0000'. \" +\n SKELETON_GUIDANCE,\n inputSchema: jsonSchema<PresentationOptions>(getSkeletonSchema(\"pptx\")),\n execute: async (options) => {\n try {\n const validated = validateDocumentInput(\"pptx\", options);\n const bytes = (await generate({\n type: \"pptx\",\n options: validated as unknown as PresentationOptions,\n outputType: \"uint8array\",\n })) as Uint8Array;\n return {\n base64: generateVerifiedBase64(\"pptx\", bytes),\n mimeType: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n };\n } catch (error) {\n throw new Error(formatToolError(\"pptx\", error));\n }\n },\n toModelOutput: ({ output }) => ({ type: \"text\", value: documentGeneratedSummary(output) }),\n});\n\nexport const xlsxTool = tool({\n description:\n \"Generate a .xlsx Excel spreadsheet. \" +\n \"The input is the workbook options directly — must include a 'worksheets' array. \" +\n \"Conventions: \" +\n \"cells are shorthand values (string, number, boolean, null) or { value, style } objects; \" +\n \"column 'width' is in Excel character units. \" +\n SKELETON_GUIDANCE,\n inputSchema: jsonSchema<WorkbookOptions>(getSkeletonSchema(\"xlsx\")),\n execute: async (options) => {\n try {\n const validated = validateDocumentInput(\"xlsx\", options);\n const formulaIssues = lintWorkbookFormulas(validated as unknown as WorkbookOptions);\n if (formulaIssues.length > 0) {\n const lines = formulaIssues.map(\n (i) => ` ${i.location}: ${i.message} — formula \"${i.formula}\"`,\n );\n throw new Error(\n `Invalid xlsx formulas:\\n${lines.join(\"\\n\")}\\nFix the formula or add the missing worksheet.`,\n );\n }\n const bytes = (await generate({\n type: \"xlsx\",\n options: validated as unknown as WorkbookOptions,\n outputType: \"uint8array\",\n })) as Uint8Array;\n return {\n base64: generateVerifiedBase64(\"xlsx\", bytes),\n mimeType: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n };\n } catch (error) {\n throw new Error(formatToolError(\"xlsx\", error));\n }\n },\n toModelOutput: ({ output }) => ({ type: \"text\", value: documentGeneratedSummary(output) }),\n});\n\nexport const schemaLookupTool = tool({\n description:\n \"Fetch the precise type definitions for office-open option fields on demand. \" +\n \"Use it before filling complex objects into the generate tools: the generate input schemas are \" +\n \"skeletons whose stubs name the definition to look up here. \" +\n \"Valid names come from the skeleton stubs, or list indexed entries with \" +\n \"`npx office-open schema index <type>` (all names with --all). \" +\n \"Returns the requested definitions plus their dependency closure as type-definition text \" +\n '(field types, \"a\" | \"b\" value enums, optional markers, one-line comments); cataloged ' +\n \"domains not requested stay as stubs, so request each domain root you need (e.g. \" +\n \"['ParagraphOptions', 'RunOptions', 'TableOptions']).\",\n inputSchema: jsonSchema<SchemaLookupInput>({\n type: \"object\",\n additionalProperties: false,\n required: [\"type\", \"definitions\"],\n properties: {\n type: {\n type: \"string\",\n enum: [\"docx\", \"pptx\", \"xlsx\"],\n description: \"Document format whose schema to slice\",\n },\n definitions: {\n type: \"array\",\n items: { type: \"string\" },\n minItems: 1,\n maxItems: 8,\n description:\n \"Definition names (TS type names, e.g. ParagraphOptions, SlideOptions, StyleOptions). \" +\n \"At most 8 per call.\",\n },\n },\n }),\n execute: async ({ type, definitions }) => {\n try {\n const slice = sliceDocumentSchema(type, definitions);\n return {\n type,\n requested: definitions,\n typeText: renderSliceTypeText(type, definitions, slice),\n };\n } catch (error) {\n if (error instanceof UnknownDefinitionError) {\n // Data, not a throw: lets the model self-correct from the suggestions.\n return {\n type,\n requested: definitions,\n error:\n `${error.message}. Closest: ${error.suggestions.join(\", \") || \"none\"}. ` +\n `List indexed entries with: npx office-open schema index ${type}`,\n suggestions: error.suggestions,\n };\n }\n throw error;\n }\n },\n});\n\nexport const officeOpenTools = {\n \"generate-docx\": docxTool,\n \"generate-pptx\": pptxTool,\n \"generate-xlsx\": xlsxTool,\n \"office-open-schema-lookup\": schemaLookupTool,\n} as const;\n"],"mappings":";;;;;;;;;;;;;;;;AAWA,SAAgB,gBAAgB,MAAc,OAAwB;CACpE,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;CAIjE,IAAI,IAAI,SAAS,mCAAmC,GAElD,OACE,4BAFW,IAAI,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,GAEH;CAUrC,IAAI,IAAI,SAAS,6BAA6B,GAE5C,OACE,sBAFW,IAAI,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,GAET;CAO/B,IAAI,IAAI,SAAS,4BAA4B,GAC3C,OACE;CAUJ,IAAI,IAAI,SAAS,cAAc,GAE7B,OAAO,IADO,SAAS,SAAS,aAAa,SAAS,SAAS,WAAW,aACzD;CAKnB,OAAO,GAAG,KAAK,YAAY,EAAE,sBAAsB;AACrD;;;;;;;;;;;;;;;ACpCA,MAAM,UAAU,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,CAAC;;AAGzD,SAAgB,uBAAuB,MAAoB,OAA2B;CACpF,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,0BAAU,IAAI,IAAoB;CACxC,KAAK,MAAM,QAAQ,OAAO,KAAK,KAAK,GAClC,QAAQ,IAAI,MAAM,QAAQ,OAAO,MAAM,KAAK,CAAC;CAE/C,MAAM,SAAS,uBAAuB,SAAS,gBAAgB,KAAM;CACrE,IAAI,OAAO,SAAS,GAAG;EACrB,MAAM,QAAQ,OAAO,KAAK,MAAM,KAAK,EAAE,KAAK,IAAI,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,EAAE,SAAS;EACrF,MAAM,IAAI,MACR,aAAa,KAAK,qHACkC,MAAM,KAAK,IAAI,GACrE;CACF;CACA,OAAO,aAAa,KAAK;AAC3B;;;;;;;;ACQA,SAAS,yBAAyB,QAAsD;CACtF,MAAM,KAAK,KAAK,KAAM,OAAO,OAAO,SAAS,IAAK,IAAI,IAAI;CAC1D,OAAO,kDAAkD,OAAO,SAAS,IAAI,GAAG;AAClF;AAEA,MAAa,WAAW,KAAK;CAC3B,aACE;CAOF,aAAa,WAA4B,kBAAkB,MAAM,CAAC;CAClE,SAAS,OAAO,YAAY;EAC1B,IAAI;GACF,MAAM,YAAY,sBAAsB,QAAQ,OAAO;GAMvD,OAAO;IACL,QAAQ,uBAAuB,QAAQ,MANpB,SAAS;KAC5B,MAAM;KACN,SAAS;KACT,YAAY;IACd,CAAC,CAE6C;IAC5C,UAAU;GACZ;EACF,SAAS,OAAO;GACd,MAAM,IAAI,MAAM,gBAAgB,QAAQ,KAAK,CAAC;EAChD;CACF;CACA,gBAAgB,EAAE,cAAc;EAAE,MAAM;EAAQ,OAAO,yBAAyB,MAAM;CAAE;AAC1F,CAAC;AAED,MAAa,WAAW,KAAK;CAC3B,aACE;CAOF,aAAa,WAAgC,kBAAkB,MAAM,CAAC;CACtE,SAAS,OAAO,YAAY;EAC1B,IAAI;GACF,MAAM,YAAY,sBAAsB,QAAQ,OAAO;GAMvD,OAAO;IACL,QAAQ,uBAAuB,QAAQ,MANpB,SAAS;KAC5B,MAAM;KACN,SAAS;KACT,YAAY;IACd,CAAC,CAE6C;IAC5C,UAAU;GACZ;EACF,SAAS,OAAO;GACd,MAAM,IAAI,MAAM,gBAAgB,QAAQ,KAAK,CAAC;EAChD;CACF;CACA,gBAAgB,EAAE,cAAc;EAAE,MAAM;EAAQ,OAAO,yBAAyB,MAAM;CAAE;AAC1F,CAAC;AAED,MAAa,WAAW,KAAK;CAC3B,aACE;CAMF,aAAa,WAA4B,kBAAkB,MAAM,CAAC;CAClE,SAAS,OAAO,YAAY;EAC1B,IAAI;GACF,MAAM,YAAY,sBAAsB,QAAQ,OAAO;GACvD,MAAM,gBAAgB,qBAAqB,SAAuC;GAClF,IAAI,cAAc,SAAS,GAAG;IAC5B,MAAM,QAAQ,cAAc,KACzB,MAAM,KAAK,EAAE,SAAS,IAAI,EAAE,QAAQ,cAAc,EAAE,QAAQ,EAC/D;IACA,MAAM,IAAI,MACR,2BAA2B,MAAM,KAAK,IAAI,EAAE,gDAC9C;GACF;GAMA,OAAO;IACL,QAAQ,uBAAuB,QAAQ,MANpB,SAAS;KAC5B,MAAM;KACN,SAAS;KACT,YAAY;IACd,CAAC,CAE6C;IAC5C,UAAU;GACZ;EACF,SAAS,OAAO;GACd,MAAM,IAAI,MAAM,gBAAgB,QAAQ,KAAK,CAAC;EAChD;CACF;CACA,gBAAgB,EAAE,cAAc;EAAE,MAAM;EAAQ,OAAO,yBAAyB,MAAM;CAAE;AAC1F,CAAC;AAED,MAAa,mBAAmB,KAAK;CACnC,aACE;CASF,aAAa,WAA8B;EACzC,MAAM;EACN,sBAAsB;EACtB,UAAU,CAAC,QAAQ,aAAa;EAChC,YAAY;GACV,MAAM;IACJ,MAAM;IACN,MAAM;KAAC;KAAQ;KAAQ;IAAM;IAC7B,aAAa;GACf;GACA,aAAa;IACX,MAAM;IACN,OAAO,EAAE,MAAM,SAAS;IACxB,UAAU;IACV,UAAU;IACV,aACE;GAEJ;EACF;CACF,CAAC;CACD,SAAS,OAAO,EAAE,MAAM,kBAAkB;EACxC,IAAI;GACF,MAAM,QAAQ,oBAAoB,MAAM,WAAW;GACnD,OAAO;IACL;IACA,WAAW;IACX,UAAU,oBAAoB,MAAM,aAAa,KAAK;GACxD;EACF,SAAS,OAAO;GACd,IAAI,iBAAiB,wBAEnB,OAAO;IACL;IACA,WAAW;IACX,OACE,GAAG,MAAM,QAAQ,aAAa,MAAM,YAAY,KAAK,IAAI,KAAK,OAAO,4DACV;IAC7D,aAAa,MAAM;GACrB;GAEF,MAAM;EACR;CACF;AACF,CAAC;AAED,MAAa,kBAAkB;CAC7B,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,6BAA6B;AAC/B"}
package/dist/cli.d.mts CHANGED
@@ -1 +1 @@
1
- export { };
1
+ export {}
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { generateToFile, parseInput } from "./generate.mjs";
2
- import { f as validateDocumentInput, i as sliceDocumentSchema, l as SCHEMAS, n as UnknownDefinitionError, o as SCHEMA_ENTRIES } from "./schemas-BKyaDGmp.mjs";
2
+ import { a as sliceDocumentSchema, g as validateDocumentInput, p as SCHEMAS, r as UnknownDefinitionError, t as renderSliceTypeText, u as SCHEMA_ENTRIES } from "./schemas-DhCWsZ8v.mjs";
3
3
  import { defineCommand, runMain } from "citty";
4
4
  //#region src/cli.ts
5
5
  const FORMATS = [
@@ -47,9 +47,11 @@ function createConvertCommand(type, defaultExt) {
47
47
  const outputPath = args.output ?? args["output-file"] ?? `output.${defaultExt}`;
48
48
  const docType = type;
49
49
  try {
50
+ const docOptions = await parseInput(jsonInput);
51
+ const validated = validateDocumentInput(docType, docOptions);
50
52
  await generateToFile(outputPath, {
51
53
  type: docType,
52
- options: validateDocumentInput(docType, await parseInput(jsonInput))
54
+ options: validated
53
55
  });
54
56
  console.log(`Generated: ${outputPath}`);
55
57
  } catch (error) {
@@ -59,111 +61,118 @@ function createConvertCommand(type, defaultExt) {
59
61
  }
60
62
  });
61
63
  }
64
+ const schemaIndexCommand = defineCommand({
65
+ meta: {
66
+ name: "index",
67
+ description: "List schema definitions (indexed lookup entries by default)"
68
+ },
69
+ args: {
70
+ format: {
71
+ type: "positional",
72
+ description: "docx | pptx | xlsx",
73
+ required: true
74
+ },
75
+ all: {
76
+ type: "boolean",
77
+ alias: "a",
78
+ description: "List every definition name"
79
+ },
80
+ json: {
81
+ type: "boolean",
82
+ description: "Machine-readable output"
83
+ }
84
+ },
85
+ run({ args }) {
86
+ const format = parseFormat(args.format);
87
+ const definitionCount = Object.keys(SCHEMAS[format].definitions ?? {}).length;
88
+ if (args.json) {
89
+ console.log(JSON.stringify(args.all ? {
90
+ format,
91
+ definitionCount,
92
+ definitions: Object.keys(SCHEMAS[format].definitions)
93
+ } : {
94
+ format,
95
+ definitionCount,
96
+ entries: SCHEMA_ENTRIES[format]
97
+ }, null, 2));
98
+ return;
99
+ }
100
+ if (args.all) {
101
+ for (const name of Object.keys(SCHEMAS[format].definitions)) console.log(name);
102
+ return;
103
+ }
104
+ const entries = SCHEMA_ENTRIES[format];
105
+ console.log(`${format}: ${entries.length} lookup entries of ${definitionCount} definitions`);
106
+ console.log();
107
+ let currentDomain = "";
108
+ for (const entry of entries) {
109
+ if (entry.domain !== currentDomain) {
110
+ currentDomain = entry.domain;
111
+ console.log(`${currentDomain}`);
112
+ }
113
+ console.log(` ${entry.name.padEnd(44)} ${entry.summary}`);
114
+ }
115
+ console.log();
116
+ console.log(`Slice a definition's fields (--json for the raw schema):`);
117
+ console.log(` office-open schema slice ${format} <Definition> [more...]`);
118
+ console.log(`List every definition name:`);
119
+ console.log(` office-open schema index ${format} --all`);
120
+ }
121
+ });
122
+ const schemaSliceCommand = defineCommand({
123
+ meta: {
124
+ name: "slice",
125
+ description: "Print a definition slice as type definitions (--json for the raw JSON schema)"
126
+ },
127
+ args: {
128
+ format: {
129
+ type: "positional",
130
+ description: "docx | pptx | xlsx",
131
+ required: true
132
+ },
133
+ definitions: {
134
+ type: "positional",
135
+ description: "One or more definition names (variadic)",
136
+ required: true
137
+ },
138
+ json: {
139
+ type: "boolean",
140
+ description: "Emit the raw draft-07 JSON schema instead"
141
+ }
142
+ },
143
+ run({ args }) {
144
+ const positional = args._;
145
+ const format = parseFormat(positional[0]);
146
+ const definitions = positional.slice(1);
147
+ if (definitions.length === 0) {
148
+ console.error("Provide at least one definition name (see `office-open schema index`).");
149
+ globalThis.process.exitCode = 1;
150
+ return;
151
+ }
152
+ try {
153
+ const slice = sliceDocumentSchema(format, definitions);
154
+ console.log(args.json ? JSON.stringify(slice, null, 2) : renderSliceTypeText(format, definitions, slice));
155
+ } catch (error) {
156
+ if (error instanceof UnknownDefinitionError) {
157
+ console.error(`${error.message}`);
158
+ if (error.suggestions.length > 0) console.error(`Closest: ${error.suggestions.join(", ")}`);
159
+ console.error(`List all names with: office-open schema index ${format} --all`);
160
+ } else throw error;
161
+ globalThis.process.exitCode = 1;
162
+ }
163
+ }
164
+ });
62
165
  const schemaCommand = defineCommand({
63
166
  meta: {
64
167
  name: "schema",
65
168
  description: "Consult the JSON schemas: list lookup entries or slice definitions"
66
169
  },
67
170
  subCommands: {
68
- index: defineCommand({
69
- meta: {
70
- name: "index",
71
- description: "List schema definitions (indexed lookup entries by default)"
72
- },
73
- args: {
74
- format: {
75
- type: "positional",
76
- description: "docx | pptx | xlsx",
77
- required: true
78
- },
79
- all: {
80
- type: "boolean",
81
- alias: "a",
82
- description: "List every definition name"
83
- },
84
- json: {
85
- type: "boolean",
86
- description: "Machine-readable output"
87
- }
88
- },
89
- run({ args }) {
90
- const format = parseFormat(args.format);
91
- const definitionCount = Object.keys(SCHEMAS[format].definitions ?? {}).length;
92
- if (args.json) {
93
- console.log(JSON.stringify(args.all ? {
94
- format,
95
- definitionCount,
96
- definitions: Object.keys(SCHEMAS[format].definitions)
97
- } : {
98
- format,
99
- definitionCount,
100
- entries: SCHEMA_ENTRIES[format]
101
- }, null, 2));
102
- return;
103
- }
104
- if (args.all) {
105
- for (const name of Object.keys(SCHEMAS[format].definitions)) console.log(name);
106
- return;
107
- }
108
- const entries = SCHEMA_ENTRIES[format];
109
- console.log(`${format}: ${entries.length} lookup entries of ${definitionCount} definitions`);
110
- console.log();
111
- let currentDomain = "";
112
- for (const entry of entries) {
113
- if (entry.domain !== currentDomain) {
114
- currentDomain = entry.domain;
115
- console.log(`${currentDomain}`);
116
- }
117
- console.log(` ${entry.name.padEnd(44)} ${entry.summary}`);
118
- }
119
- console.log();
120
- console.log(`Slice a definition's JSON schema:`);
121
- console.log(` office-open schema slice ${format} <Definition> [more...]`);
122
- console.log(`List every definition name:`);
123
- console.log(` office-open schema index ${format} --all`);
124
- }
125
- }),
126
- slice: defineCommand({
127
- meta: {
128
- name: "slice",
129
- description: "Print the JSON schema slice for one or more definitions"
130
- },
131
- args: {
132
- format: {
133
- type: "positional",
134
- description: "docx | pptx | xlsx",
135
- required: true
136
- },
137
- definitions: {
138
- type: "positional",
139
- description: "One or more definition names (variadic)",
140
- required: true
141
- }
142
- },
143
- run({ args }) {
144
- const positional = args._;
145
- const format = parseFormat(positional[0]);
146
- const definitions = positional.slice(1);
147
- if (definitions.length === 0) {
148
- console.error("Provide at least one definition name (see `office-open schema index`).");
149
- globalThis.process.exitCode = 1;
150
- return;
151
- }
152
- try {
153
- console.log(JSON.stringify(sliceDocumentSchema(format, definitions), null, 2));
154
- } catch (error) {
155
- if (error instanceof UnknownDefinitionError) {
156
- console.error(`${error.message}`);
157
- if (error.suggestions.length > 0) console.error(`Closest: ${error.suggestions.join(", ")}`);
158
- console.error(`List all names with: office-open schema index ${format} --all`);
159
- } else throw error;
160
- globalThis.process.exitCode = 1;
161
- }
162
- }
163
- })
171
+ index: schemaIndexCommand,
172
+ slice: schemaSliceCommand
164
173
  }
165
174
  });
166
- runMain(defineCommand({
175
+ const mainCommand = defineCommand({
167
176
  meta: {
168
177
  name: "office-open",
169
178
  version: "0.10.15",
@@ -185,7 +194,8 @@ runMain(defineCommand({
185
194
  ]
186
195
  } },
187
196
  async run() {}
188
- }));
197
+ });
198
+ runMain(mainCommand);
189
199
  //#endregion
190
200
  export {};
191
201
 
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { defineCommand, runMain } from \"citty\";\n\nimport { generateToFile, parseInput } from \"./generate\";\nimport {\n SCHEMA_ENTRIES,\n UnknownDefinitionError,\n sliceDocumentSchema,\n validateDocumentInput,\n} from \"./schemas\";\nimport { SCHEMAS, type DocumentType } from \"./schemas/schemas\";\n\nconst FORMATS: readonly DocumentType[] = [\"docx\", \"pptx\", \"xlsx\"];\n\n/** Parse and validate the format positional (citty positionals cannot be enums). */\nfunction parseFormat(raw: string | undefined): DocumentType {\n if (raw && (FORMATS as readonly string[]).includes(raw)) return raw as DocumentType;\n console.error(`Unknown format \"${raw ?? \"\"}\" — expected one of: ${FORMATS.join(\", \")}`);\n globalThis.process.exitCode = 1;\n throw new Error(\"invalid format\");\n}\n\nfunction createConvertCommand(type: string, defaultExt: string) {\n return defineCommand({\n meta: {\n name: type,\n description: `Generate a .${defaultExt} file from JSON`,\n },\n args: {\n input: {\n type: \"positional\",\n description: \"JSON string or path to JSON file\",\n required: true,\n },\n output: {\n type: \"positional\",\n description: `Output file path (default: output.${defaultExt})`,\n required: false,\n },\n \"input-file\": {\n type: \"string\",\n description: \"Read JSON from file (alternative to positional input)\",\n alias: [\"i\"],\n },\n \"output-file\": {\n type: \"string\",\n description: \"Output file path (alternative to positional output)\",\n alias: [\"o\"],\n },\n },\n async run({ args }) {\n const jsonInput = (args.input ?? args[\"input-file\"]) as string;\n const outputPath = (args.output ?? args[\"output-file\"] ?? `output.${defaultExt}`) as string;\n const docType = type as \"docx\" | \"pptx\" | \"xlsx\";\n\n try {\n const docOptions = await parseInput(jsonInput);\n const validated = validateDocumentInput(docType, docOptions);\n await generateToFile(outputPath, {\n type: docType,\n options: validated,\n });\n console.log(`Generated: ${outputPath}`);\n } catch (error) {\n // Expected user errors (bad JSON, schema violations) print as a single line;\n // rethrowing would make runMain dump them with a stack trace.\n console.error(`Error: ${(error as Error).message}`);\n globalThis.process.exitCode = 1;\n }\n },\n });\n}\n\nconst schemaIndexCommand = defineCommand({\n meta: {\n name: \"index\",\n description: \"List schema definitions (indexed lookup entries by default)\",\n },\n args: {\n format: { type: \"positional\", description: \"docx | pptx | xlsx\", required: true },\n all: { type: \"boolean\", alias: \"a\", description: \"List every definition name\" },\n json: { type: \"boolean\", description: \"Machine-readable output\" },\n },\n run({ args }) {\n const format = parseFormat(args.format as string | undefined);\n const definitionCount = Object.keys(\n (SCHEMAS[format].definitions as Record<string, unknown>) ?? {},\n ).length;\n\n if (args.json) {\n console.log(\n JSON.stringify(\n args.all\n ? {\n format,\n definitionCount,\n definitions: Object.keys(SCHEMAS[format].definitions as Record<string, unknown>),\n }\n : { format, definitionCount, entries: SCHEMA_ENTRIES[format] },\n null,\n 2,\n ),\n );\n return;\n }\n\n if (args.all) {\n for (const name of Object.keys(SCHEMAS[format].definitions as Record<string, unknown>)) {\n console.log(name);\n }\n return;\n }\n\n const entries = SCHEMA_ENTRIES[format];\n console.log(`${format}: ${entries.length} lookup entries of ${definitionCount} definitions`);\n console.log();\n let currentDomain = \"\";\n for (const entry of entries) {\n if (entry.domain !== currentDomain) {\n currentDomain = entry.domain;\n console.log(`${currentDomain}`);\n }\n console.log(` ${entry.name.padEnd(44)} ${entry.summary}`);\n }\n console.log();\n console.log(`Slice a definition's JSON schema:`);\n console.log(` office-open schema slice ${format} <Definition> [more...]`);\n console.log(`List every definition name:`);\n console.log(` office-open schema index ${format} --all`);\n },\n});\n\nconst schemaSliceCommand = defineCommand({\n meta: {\n name: \"slice\",\n description: \"Print the JSON schema slice for one or more definitions\",\n },\n args: {\n format: { type: \"positional\", description: \"docx | pptx | xlsx\", required: true },\n definitions: {\n type: \"positional\",\n description: \"One or more definition names (variadic)\",\n required: true,\n },\n },\n run({ args }) {\n // citty does not type variadic positionals; args._ keeps every raw positional\n // (format first), so slice the tail off it instead of the typed args.\n const positional = args._ as string[];\n const format = parseFormat(positional[0]);\n const definitions = positional.slice(1);\n if (definitions.length === 0) {\n console.error(\"Provide at least one definition name (see `office-open schema index`).\");\n globalThis.process.exitCode = 1;\n return;\n }\n try {\n console.log(JSON.stringify(sliceDocumentSchema(format, definitions), null, 2));\n } catch (error) {\n if (error instanceof UnknownDefinitionError) {\n console.error(`${error.message}`);\n if (error.suggestions.length > 0) {\n console.error(`Closest: ${error.suggestions.join(\", \")}`);\n }\n console.error(`List all names with: office-open schema index ${format} --all`);\n } else {\n throw error;\n }\n globalThis.process.exitCode = 1;\n }\n },\n});\n\nconst schemaCommand = defineCommand({\n meta: {\n name: \"schema\",\n description: \"Consult the JSON schemas: list lookup entries or slice definitions\",\n },\n subCommands: { index: schemaIndexCommand, slice: schemaSliceCommand },\n});\n\nconst mainCommand = defineCommand({\n meta: {\n name: \"office-open\",\n version: \"0.10.15\",\n description: \"Generate Office files (.docx, .pptx, .xlsx) from JSON\",\n },\n subCommands: {\n docx: createConvertCommand(\"docx\", \"docx\"),\n pptx: createConvertCommand(\"pptx\", \"pptx\"),\n xlsx: createConvertCommand(\"xlsx\", \"xlsx\"),\n schema: schemaCommand,\n },\n args: {\n type: {\n type: \"enum\",\n description: \"File type to generate\",\n options: [\"docx\", \"pptx\", \"xlsx\"],\n },\n },\n async run() {\n // citty shows usage when no subcommand is matched\n },\n});\n\nvoid runMain(mainCommand);\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { defineCommand, runMain } from \"citty\";\n\nimport { generateToFile, parseInput } from \"./generate\";\nimport {\n SCHEMA_ENTRIES,\n UnknownDefinitionError,\n renderSliceTypeText,\n sliceDocumentSchema,\n validateDocumentInput,\n} from \"./schemas\";\nimport { SCHEMAS, type DocumentType } from \"./schemas/schemas\";\n\nconst FORMATS: readonly DocumentType[] = [\"docx\", \"pptx\", \"xlsx\"];\n\n/** Parse and validate the format positional (citty positionals cannot be enums). */\nfunction parseFormat(raw: string | undefined): DocumentType {\n if (raw && (FORMATS as readonly string[]).includes(raw)) return raw as DocumentType;\n console.error(`Unknown format \"${raw ?? \"\"}\" — expected one of: ${FORMATS.join(\", \")}`);\n globalThis.process.exitCode = 1;\n throw new Error(\"invalid format\");\n}\n\nfunction createConvertCommand(type: string, defaultExt: string) {\n return defineCommand({\n meta: {\n name: type,\n description: `Generate a .${defaultExt} file from JSON`,\n },\n args: {\n input: {\n type: \"positional\",\n description: \"JSON string or path to JSON file\",\n required: true,\n },\n output: {\n type: \"positional\",\n description: `Output file path (default: output.${defaultExt})`,\n required: false,\n },\n \"input-file\": {\n type: \"string\",\n description: \"Read JSON from file (alternative to positional input)\",\n alias: [\"i\"],\n },\n \"output-file\": {\n type: \"string\",\n description: \"Output file path (alternative to positional output)\",\n alias: [\"o\"],\n },\n },\n async run({ args }) {\n const jsonInput = (args.input ?? args[\"input-file\"]) as string;\n const outputPath = (args.output ?? args[\"output-file\"] ?? `output.${defaultExt}`) as string;\n const docType = type as \"docx\" | \"pptx\" | \"xlsx\";\n\n try {\n const docOptions = await parseInput(jsonInput);\n const validated = validateDocumentInput(docType, docOptions);\n await generateToFile(outputPath, {\n type: docType,\n options: validated,\n });\n console.log(`Generated: ${outputPath}`);\n } catch (error) {\n // Expected user errors (bad JSON, schema violations) print as a single line;\n // rethrowing would make runMain dump them with a stack trace.\n console.error(`Error: ${(error as Error).message}`);\n globalThis.process.exitCode = 1;\n }\n },\n });\n}\n\nconst schemaIndexCommand = defineCommand({\n meta: {\n name: \"index\",\n description: \"List schema definitions (indexed lookup entries by default)\",\n },\n args: {\n format: { type: \"positional\", description: \"docx | pptx | xlsx\", required: true },\n all: { type: \"boolean\", alias: \"a\", description: \"List every definition name\" },\n json: { type: \"boolean\", description: \"Machine-readable output\" },\n },\n run({ args }) {\n const format = parseFormat(args.format as string | undefined);\n const definitionCount = Object.keys(\n (SCHEMAS[format].definitions as Record<string, unknown>) ?? {},\n ).length;\n\n if (args.json) {\n console.log(\n JSON.stringify(\n args.all\n ? {\n format,\n definitionCount,\n definitions: Object.keys(SCHEMAS[format].definitions as Record<string, unknown>),\n }\n : { format, definitionCount, entries: SCHEMA_ENTRIES[format] },\n null,\n 2,\n ),\n );\n return;\n }\n\n if (args.all) {\n for (const name of Object.keys(SCHEMAS[format].definitions as Record<string, unknown>)) {\n console.log(name);\n }\n return;\n }\n\n const entries = SCHEMA_ENTRIES[format];\n console.log(`${format}: ${entries.length} lookup entries of ${definitionCount} definitions`);\n console.log();\n let currentDomain = \"\";\n for (const entry of entries) {\n if (entry.domain !== currentDomain) {\n currentDomain = entry.domain;\n console.log(`${currentDomain}`);\n }\n console.log(` ${entry.name.padEnd(44)} ${entry.summary}`);\n }\n console.log();\n console.log(`Slice a definition's fields (--json for the raw schema):`);\n console.log(` office-open schema slice ${format} <Definition> [more...]`);\n console.log(`List every definition name:`);\n console.log(` office-open schema index ${format} --all`);\n },\n});\n\nconst schemaSliceCommand = defineCommand({\n meta: {\n name: \"slice\",\n description: \"Print a definition slice as type definitions (--json for the raw JSON schema)\",\n },\n args: {\n format: { type: \"positional\", description: \"docx | pptx | xlsx\", required: true },\n definitions: {\n type: \"positional\",\n description: \"One or more definition names (variadic)\",\n required: true,\n },\n json: { type: \"boolean\", description: \"Emit the raw draft-07 JSON schema instead\" },\n },\n run({ args }) {\n // citty does not type variadic positionals; args._ keeps every raw positional\n // (format first), so slice the tail off it instead of the typed args.\n const positional = args._ as string[];\n const format = parseFormat(positional[0]);\n const definitions = positional.slice(1);\n if (definitions.length === 0) {\n console.error(\"Provide at least one definition name (see `office-open schema index`).\");\n globalThis.process.exitCode = 1;\n return;\n }\n try {\n const slice = sliceDocumentSchema(format, definitions);\n console.log(\n args.json\n ? JSON.stringify(slice, null, 2)\n : renderSliceTypeText(format, definitions, slice),\n );\n } catch (error) {\n if (error instanceof UnknownDefinitionError) {\n console.error(`${error.message}`);\n if (error.suggestions.length > 0) {\n console.error(`Closest: ${error.suggestions.join(\", \")}`);\n }\n console.error(`List all names with: office-open schema index ${format} --all`);\n } else {\n throw error;\n }\n globalThis.process.exitCode = 1;\n }\n },\n});\n\nconst schemaCommand = defineCommand({\n meta: {\n name: \"schema\",\n description: \"Consult the JSON schemas: list lookup entries or slice definitions\",\n },\n subCommands: { index: schemaIndexCommand, slice: schemaSliceCommand },\n});\n\nconst mainCommand = defineCommand({\n meta: {\n name: \"office-open\",\n version: \"0.10.15\",\n description: \"Generate Office files (.docx, .pptx, .xlsx) from JSON\",\n },\n subCommands: {\n docx: createConvertCommand(\"docx\", \"docx\"),\n pptx: createConvertCommand(\"pptx\", \"pptx\"),\n xlsx: createConvertCommand(\"xlsx\", \"xlsx\"),\n schema: schemaCommand,\n },\n args: {\n type: {\n type: \"enum\",\n description: \"File type to generate\",\n options: [\"docx\", \"pptx\", \"xlsx\"],\n },\n },\n async run() {\n // citty shows usage when no subcommand is matched\n },\n});\n\nvoid runMain(mainCommand);\n"],"mappings":"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"}
@@ -1,9 +1,8 @@
1
- import { GroupOptions, MediaTransformation, ParagraphOptions, PictureOptions, ShapeCoreOptions, ShapeOptions, SmartArtOptions, TableOptions } from "@office-open/docx";
1
+ import { GroupOptions, MediaTransformation, ParagraphOptions, PictureOptions, ShapeCoreOptions, ShapeOptions, ShapeTextBoxChild, SmartArtOptions, TableOptions } from "@office-open/docx";
2
2
  import { ConnectorOptions, GroupOptions as GroupOptions$1, PictureOptions as PictureOptions$1, ShapeOptions as ShapeOptions$1, SmartArtOptions as SmartArtOptions$1, TableOptions as TableOptions$1 } from "@office-open/pptx";
3
3
  import { ColumnOptions, ConnectorOptions as ConnectorOptions$1, GroupOptions as GroupOptions$2, MergeCellOptions, PictureOptions as PictureOptions$2, RowOptions, ShapeOptions as ShapeOptions$2 } from "@office-open/xlsx";
4
4
  import { ParagraphDescriptorOptions, UniversalMeasure } from "@office-open/core";
5
- import { EffectDagOptions, EffectListOptions, FillOptions, OutlineOptions, PresetGeometryOptions, Scene3DOptions, Shape3DOptions, TextBodyOptions } from "@office-open/core/drawing";
6
-
5
+ import { EffectDagOptions, EffectListOptions, FillOptions, OutlineOptions, PresetGeometryOptions, Scene3DOptions, Shape3DOptions, ShapeType, TextBodyOptions } from "@office-open/core/drawing";
7
6
  //#region src/convert/picture.d.ts
8
7
  declare function toDocxPicture(source: PictureOptions$1): PictureOptions;
9
8
  declare function toDocxPicture(source: PictureOptions$2): PictureOptions;
@@ -15,17 +14,20 @@ declare function toXlsxPicture(source: PictureOptions$1): PictureOptions$2;
15
14
  //#region src/convert/shape.d.ts
16
15
  type DocxShapeOptions = ShapeOptions;
17
16
  interface ShapeContent {
18
- fill?: FillOptions;
17
+ fill?: FillOptions | null;
19
18
  outline?: OutlineOptions;
20
19
  effects?: EffectListOptions;
21
20
  effectDag?: EffectDagOptions;
22
21
  scene3d?: Scene3DOptions;
23
22
  shape3d?: Shape3DOptions;
24
23
  }
25
- declare function pickContent<T extends ShapeContent>(source: T): ShapeContent;
26
- declare function toPresetGeometry(g: string | PresetGeometryOptions | undefined): PresetGeometryOptions | undefined;
24
+ type PickedContent = Omit<ShapeContent, "fill"> & {
25
+ fill?: FillOptions;
26
+ };
27
+ declare function pickContent<T extends ShapeContent>(source: T): PickedContent;
28
+ declare function toPresetGeometry(g: ShapeType | PresetGeometryOptions | undefined): PresetGeometryOptions | undefined;
27
29
  declare function textBodyToDocxChildren(textBody: TextBodyOptions): ParagraphOptions[] | string[];
28
- declare function docxToTextBody(children: (ParagraphOptions | string)[] | undefined, bodyProperties: TextBodyOptions["bodyProperties"]): TextBodyOptions | undefined;
30
+ declare function docxToTextBody(children: ShapeTextBoxChild[] | undefined, bodyProperties: TextBodyOptions["bodyProperties"]): TextBodyOptions | undefined;
29
31
  interface DocxShapeParts {
30
32
  data: ShapeCoreOptions;
31
33
  transformation: MediaTransformation;