import { classifyTemplateField, computeFieldDiff, createSqlClient, getEffectiveMappedKeys, extractTemplateFields, getActiveTemplateVersions, getDuplicateFieldNames, getMappingsForVersion, getUnknownFieldNames, summarizeIssues } from './pdf-audit-utils.ts'; async function main() { const sql = createSqlClient(); try { const templates = await getActiveTemplateVersions(sql); const results = []; for (const template of templates) { const fields = extractTemplateFields(template.schemaJson); const previousVersion = ( await sql` select schema_json as "schemaJson", version from crm_document_template_versions where template_id = ${template.templateId} and deleted_at is null and id <> ${template.versionId} order by created_at desc limit 1 ` )[0] ?? null; const previousFields = previousVersion ? extractTemplateFields(previousVersion.schemaJson) : []; const { mappings } = await getMappingsForVersion(sql, template.versionId); const fieldNames = new Set(fields.map((field) => field.name).filter(Boolean)); const mappedKeys = getEffectiveMappedKeys(fields, mappings); const duplicateFields = getDuplicateFieldNames(fields); const unknownFields = getUnknownFieldNames(fields, mappedKeys); const fieldDiff = computeFieldDiff(fields, previousFields); const requiredTableFields = ['quotation_price_data']; const missingRequiredTableFields = requiredTableFields.filter( (fieldName) => !fields.some((field) => field.name === fieldName && field.type === 'table') ); const tableFields = fields.filter((field) => field.type === 'table').map((field) => field.name); const dynamicTopicFields = fields .filter((field) => field.name === 'topic' || field.name === 'data_topic') .map((field) => field.name); const placeholderTokens = new Set(fields.flatMap((field) => field.tokens)); const missingMappings = [...placeholderTokens].filter( (token) => token !== 'item_topic' && !mappedKeys.has(token) ); const orphanMappings = mappings .filter( (mapping) => !placeholderTokens.has(mapping.placeholderKey) && !fieldNames.has(mapping.placeholderKey) && mapping.placeholderKey !== 'items_table' ) .map((mapping) => mapping.placeholderKey); const classifications = fields.map((field) => ({ name: field.name, type: field.type, classification: classifyTemplateField(field, mappedKeys) })); const status = summarizeIssues([ duplicateFields.length ? 'FAIL' : 'PASS', missingRequiredTableFields.length ? 'FAIL' : 'PASS', missingMappings.length ? 'FAIL' : 'PASS', orphanMappings.length ? 'WARNING' : 'PASS', unknownFields.length ? 'WARNING' : 'PASS' ]); results.push({ templateName: template.templateName, version: template.version, previousVersion: previousVersion?.version ?? null, organizationId: template.organizationId, schemaFieldCount: fields.length, tableFields, missingRequiredTableFields, dynamicTopicFields, deletedFields: fieldDiff.deletedFields, newFields: fieldDiff.newFields, duplicateFields, unknownFields, classifications, missingMappings, orphanMappings, mappedFieldCount: mappings.length, status }); } const overallStatus = summarizeIssues( results.map((result) => result.status) as Array<'PASS' | 'WARNING' | 'FAIL'> ); console.log( JSON.stringify( { audit: 'template-inventory', overallStatus, templateCount: results.length, results }, null, 2 ) ); } finally { await sql.end({ timeout: 5 }); } } main().catch((error) => { console.error(`[audit-pdf-template-inventory] ${error.message}`); process.exit(1); });