import { StringEnum } from "@earendil-works/pi-ai"; import { Type } from "typebox"; import { DOCX_CONTRACT_VERSION } from "./contracts.ts"; const OfficePath = Type.String({ minLength: 1, description: "Office document path; leading @, ~, relative, and absolute paths are supported." }); const Story = StringEnum(["main", "header", "footer", "footnote", "endnote", "comment", "textbox"] as const); const Sha256 = Type.String({ pattern: "^[a-fA-F0-9]{64}$" }); const SemanticHash = Type.String({ pattern: "^[a-fA-F0-9]{16}$" }); const TableCellSelector = Type.Object({ kind: Type.Literal("tableCell"), story: Type.Optional(Story), table: Type.Integer({ minimum: 1 }), row: Type.Integer({ minimum: 1 }), cell: Type.Integer({ minimum: 1 }), expectedHash: Type.Optional(SemanticHash) }, { additionalProperties: false }); const TableRowSelector = Type.Object({ kind: Type.Literal("tableRow"), story: Type.Optional(Story), table: Type.Integer({ minimum: 1 }), row: Type.Integer({ minimum: 1 }), expectedHash: Type.Optional(SemanticHash) }, { additionalProperties: false }); const Selector = Type.Union([ Type.Object({ kind: Type.Literal("paragraphId"), paragraphId: Type.String({ minLength: 1 }), story: Type.Optional(Story), expectedHash: Type.Optional(SemanticHash) }, { additionalProperties: false }), Type.Object({ kind: Type.Literal("path"), story: Story, path: Type.String({ minLength: 1 }), expectedHash: Type.Optional(SemanticHash) }, { additionalProperties: false }), Type.Object({ kind: Type.Literal("text"), text: Type.String({ minLength: 1 }), story: Type.Optional(Story), occurrence: Type.Optional(Type.Integer({ minimum: 1 })), expectedCount: Type.Optional(Type.Integer({ minimum: 1 })), before: Type.Optional(Type.String()), after: Type.Optional(Type.String()) }, { additionalProperties: false }), Type.Object({ kind: Type.Literal("bookmark"), name: Type.String({ minLength: 1 }) }, { additionalProperties: false }), Type.Object({ kind: Type.Literal("contentControl"), tag: Type.Optional(Type.String()), title: Type.Optional(Type.String()) }, { additionalProperties: false }), TableCellSelector, TableRowSelector, ]); const CharacterFormatting = Type.Object({ bold: Type.Optional(Type.Boolean()), italic: Type.Optional(Type.Boolean()), underline: Type.Optional(StringEnum(["none", "single", "double"] as const)), fontFamily: Type.Optional(Type.String({ minLength: 1, maxLength: 255 })), fontSizePoints: Type.Optional(Type.Number({ exclusiveMinimum: 0, maximum: 409 })), color: Type.Optional(Type.String({ pattern: "^[A-Fa-f0-9]{6}$" })) }, { additionalProperties: false, minProperties: 1 }); const ParagraphFormatting = Type.Object({ style: Type.Optional(Type.String({ minLength: 1 })), alignment: Type.Optional(StringEnum(["left", "center", "right", "both"] as const)), spacingBeforePoints: Type.Optional(Type.Number({ minimum: 0 })), spacingAfterPoints: Type.Optional(Type.Number({ minimum: 0 })), lineSpacing: Type.Optional(Type.Number({ exclusiveMinimum: 0 })), indentLeftPoints: Type.Optional(Type.Number()), indentRightPoints: Type.Optional(Type.Number()), firstLineIndentPoints: Type.Optional(Type.Number()) }, { additionalProperties: false, minProperties: 1 }); const Operation = Type.Union([ Type.Object({ type: Type.Literal("replaceText"), find: Type.String({ minLength: 1 }), replacement: Type.String(), story: Type.Optional(Story), selector: Type.Optional(Selector), expectedCount: Type.Integer({ minimum: 1 }) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("insertParagraph"), selector: Selector, position: StringEnum(["before", "after"] as const), text: Type.String(), style: Type.Optional(Type.String()) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("deleteParagraph"), selector: Selector, expectedText: Type.Optional(Type.String()) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("setTableCellText"), selector: TableCellSelector, text: Type.String(), expectedText: Type.Optional(Type.String()) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("insertTableRow"), selector: TableRowSelector, position: StringEnum(["before", "after"] as const), cells: Type.Array(Type.String(), { minItems: 1, maxItems: 100 }) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("deleteTableRow"), selector: TableRowSelector }, { additionalProperties: false }), Type.Object({ type: Type.Literal("setCharacterFormatting"), selector: Selector, formatting: CharacterFormatting }, { additionalProperties: false }), Type.Object({ type: Type.Literal("setParagraphFormatting"), selector: Selector, formatting: ParagraphFormatting }, { additionalProperties: false }), Type.Object({ type: Type.Literal("setHyperlink"), selector: Selector, text: Type.String({ minLength: 1 }), target: Type.String({ minLength: 1, maxLength: 4096 }), tooltip: Type.Optional(Type.String({ maxLength: 255 })) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("removeHyperlink"), selector: Selector, text: Type.Optional(Type.String()) }, { additionalProperties: false }), Type.Object({ type: Type.Literal("setCoreProperties"), properties: Type.Object({ title: Type.Optional(Type.String()), subject: Type.Optional(Type.String()), creator: Type.Optional(Type.String()), keywords: Type.Optional(Type.String()), description: Type.Optional(Type.String()), category: Type.Optional(Type.String()) }, { additionalProperties: false, minProperties: 1 }) }, { additionalProperties: false }), ]); const Limits = Type.Optional(Type.Object({ maxArchiveBytes: Type.Optional(Type.Integer({ minimum: 1 })), maxEntries: Type.Optional(Type.Integer({ minimum: 1 })), maxEntryBytes: Type.Optional(Type.Integer({ minimum: 1 })), maxUncompressedBytes: Type.Optional(Type.Integer({ minimum: 1 })), maxCompressionRatio: Type.Optional(Type.Number({ exclusiveMinimum: 0 })), maxXmlBytes: Type.Optional(Type.Integer({ minimum: 1 })), maxParagraphs: Type.Optional(Type.Integer({ minimum: 1 })), maxTables: Type.Optional(Type.Integer({ minimum: 1 })), maxRuns: Type.Optional(Type.Integer({ minimum: 1 })), maxSearchMatches: Type.Optional(Type.Integer({ minimum: 1 })), maxVisibleOutputChars: Type.Optional(Type.Integer({ minimum: 1000 })), maxOperations: Type.Optional(Type.Integer({ minimum: 1 })) }, { additionalProperties: false })); export const InspectSchema = Type.Object({ path: OfficePath, includeHiddenData: Type.Optional(Type.Boolean()), limits: Limits }, { additionalProperties: false }); export const ReadSchema = Type.Object({ path: OfficePath, stories: Type.Optional(Type.Array(Story, { maxItems: 7 })), selector: Type.Optional(Selector), query: Type.Optional(Type.String({ minLength: 1 })), exact: Type.Optional(Type.Boolean()), maxBlocks: Type.Optional(Type.Integer({ minimum: 1, maximum: 10000 })), includeHiddenData: Type.Optional(Type.Boolean()), limits: Limits }, { additionalProperties: false }); export const RenderSchema = Type.Object({ path: OfficePath, pages: Type.Optional(Type.String({ pattern: "^(?:all|[0-9,-]+)$" })), dpi: Type.Optional(Type.Integer({ minimum: 72, maximum: 300 })), timeoutMs: Type.Optional(Type.Integer({ minimum: 1000, maximum: 600000 })), limits: Limits }, { additionalProperties: false }); export const EditSchema = Type.Object({ path: OfficePath, schemaVersion: Type.Optional(Type.Literal(DOCX_CONTRACT_VERSION)), expectedSourceSha256: Type.Optional(Sha256), dryRun: Type.Optional(Type.Boolean({ default: true })), operations: Type.Array(Operation, { minItems: 1, maxItems: 1000 }), timeoutMs: Type.Optional(Type.Integer({ minimum: 1000, maximum: 600000 })), limits: Limits }, { additionalProperties: false }); export const DiffSchema = Type.Object({ beforePath: OfficePath, afterPath: OfficePath, includeFormatting: Type.Optional(Type.Boolean()), includePackageParts: Type.Optional(Type.Boolean()), renderPages: Type.Optional(Type.String({ pattern: "^(?:all|[0-9,-]+)$" })), timeoutMs: Type.Optional(Type.Integer({ minimum: 1000, maximum: 600000 })), maxChanges: Type.Optional(Type.Integer({ minimum: 1, maximum: 10000 })), limits: Limits }, { additionalProperties: false }); export const ValidateSchema = Type.Object({ path: OfficePath, baselinePath: Type.Optional(OfficePath), expectedChangedParts: Type.Optional(Type.Array(Type.String(), { maxItems: 1000 })), renderPages: Type.Optional(Type.String()), timeoutMs: Type.Optional(Type.Integer({ minimum: 1000, maximum: 600000 })), limits: Limits }, { additionalProperties: false }); export const CommitSchema = Type.Object({ revisionId: Type.String({ minLength: 1 }), destinationPath: Type.Optional(Type.String()), overwrite: Type.Optional(Type.Boolean()), inPlace: Type.Optional(Type.Boolean()), expectedSourceSha256: Sha256, expectedDestinationSha256: Type.Optional(Sha256) }, { additionalProperties: false });