import { z } from "zod"; import type { DocxEditorRuntime } from "@docx-editor.dev/editor-api/browser"; export const proposalSchema = z.object({ replacement: z.string().min(1).max(100), reason: z.string().trim().min(1).max(500), }).strict(); export type PaymentTask = { clause: string; source: string; minDays: number; maxDays: number; }; export function validatePaymentProposal(value: unknown, task: PaymentTask) { const result = proposalSchema.safeParse(value); if (!result.success) { throw new Error("Provide replacement text and a reason of 1–500 characters, with no extra fields."); } const proposal = result.data; const match = /^within ([1-9]\d*) days$/.exec(proposal.replacement); const days = match ? Number(match[1]) : NaN; if (!Number.isSafeInteger(days) || days < task.minDays || days > task.maxDays) { throw new Error(`Use a payment deadline between ${task.minDays} and ${task.maxDays} days.`); } return proposal; } export async function applyPaymentProposal( runtime: Pick, value: unknown, task: PaymentTask, ) { const proposal = validatePaymentProposal(value, task); await runtime.run(async (context) => { const clauses = context.document.body.search(task.clause, { matchCase: true }); clauses.load("items"); await context.sync(); if (clauses.items.length !== 1) { throw new Error("The payment clause changed or is not unique. Select it again."); } const matches = clauses.items[0]!.search(task.source, { matchCase: true }); matches.load("items"); await context.sync(); if (matches.items.length !== 1) { throw new Error("The selected clause needs one matching deadline."); } context.document.changeTrackingMode = "TrackMineOnly"; matches.items[0]!.insertText(proposal.replacement, "Replace"); await context.sync(); }); return proposal; }