import { buildAuditPayload, buildComputedSnapshotPayload, computePayloadHash, createSqlClient, summarizeIssues, writeAuditArtifact } from './pdf-audit-utils.ts'; function pickComparablePayload(payload: Record) { return { templateInput: payload.templateInput, topicInputKeys: payload.topicInputs && typeof payload.topicInputs === 'object' ? Object.keys(payload.topicInputs as Record).sort() : [] }; } async function main() { const sql = createSqlClient(); try { const payload = await buildAuditPayload(sql); const previewPayload = pickComparablePayload({ templateInput: payload.templateInput, topicInputs: payload.topicInputs }); const generationPayload = pickComparablePayload({ templateInput: payload.templateInput, topicInputs: payload.topicInputs }); const computedSnapshot = buildComputedSnapshotPayload(payload, { generatedAt: 'audit-generated', generatedBy: 'audit-suite' }); const snapshotPayload = pickComparablePayload({ templateInput: computedSnapshot.templateInput, topicInputs: payload.topicInputs }); const previewHash = computePayloadHash(previewPayload); const generationHash = computePayloadHash(generationPayload); const snapshotHash = computePayloadHash(snapshotPayload); const mismatches = [ previewHash !== generationHash ? 'preview:generation' : null, generationHash !== snapshotHash ? 'generation:snapshot' : null, previewHash !== snapshotHash ? 'preview:snapshot' : null ].filter(Boolean); const signatureKeys = ['preparedBy', 'approvedBy', 'authorizedBy']; const signatureParity = signatureKeys.every((key) => { const signatures = payload.documentData.signatures as Record; return key in signatures; }); const priceTable = payload.templateInput.quotation_price_data; const firstPriceRow = Array.isArray(priceTable) && Array.isArray(priceTable[0]) ? priceTable[0] : null; const priceTableParity = Array.isArray(priceTable) && Array.isArray(firstPriceRow) && firstPriceRow[0] === 'Price (Exclude VAT)' && typeof firstPriceRow[1] === 'string' && typeof firstPriceRow[3] === 'string'; const status = summarizeIssues([ mismatches.length ? 'FAIL' : 'PASS', signatureParity ? 'PASS' : 'FAIL', priceTableParity ? 'PASS' : 'FAIL' ]); const report = { audit: 'payload-parity', overallStatus: status, quotationCode: payload.quotationCode, previewHash, generationHash, snapshotHash, mismatches, comparableFieldCount: Object.keys(payload.templateInput).length, topicInputKeyCount: Object.keys(payload.topicInputs).length, signatureParity, priceTableParity, priceTableRowCount: Array.isArray(priceTable) ? priceTable.length : 0, priceTableCurrency: Array.isArray(firstPriceRow) ? firstPriceRow[3] ?? null : null }; const artifactPath = await writeAuditArtifact('payload-parity-report.json', report); console.log(JSON.stringify({ ...report, artifactPath }, null, 2)); } finally { await sql.end({ timeout: 5 }); } } main().catch((error) => { console.error(`[audit-payload-parity] ${error.message}`); process.exit(1); });