import { createSqlClient, extractTemplateFields, getActiveTemplateVersions, getEffectiveMappedKeys, getMappingsForVersion, summarizeIssues } from './pdf-audit-utils.ts'; function classifyToken(token: string, mappedKeys: Set) { if (token === 'topic' || token === 'data_topic') { return 'dynamic_topic_field'; } if (token === 'item_topic') { return 'dynamic_topic_field'; } if (token.startsWith('topic_') || token.startsWith('item_topic_')) { return 'dynamic_runtime_field'; } if (mappedKeys.has(token)) { return 'mapped_field'; } return 'unmapped_field'; } 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 { mappings, columns } = await getMappingsForVersion(sql, template.versionId); const mappedKeys = getEffectiveMappedKeys(fields, mappings); const fieldNames = new Set(fields.map((field) => field.name).filter(Boolean)); const schemaTokens = [...new Set(fields.flatMap((field) => field.tokens))].sort(); const coverage = schemaTokens.map((token) => { const mapping = mappings.find((candidate) => candidate.placeholderKey === token) ?? mappings.find( (candidate) => fields.some( (field) => field.name === candidate.placeholderKey && field.tokens.length === 1 && field.tokens[0] === token ) ) ?? null; return { token, classification: classifyToken(token, mappedKeys), sourcePath: mapping?.sourcePath ?? null, dataType: mapping?.dataType ?? null }; }); const unmappedFields = coverage.filter((item) => item.classification === 'unmapped_field'); const orphanMappings = mappings .filter( (mapping) => !schemaTokens.includes(mapping.placeholderKey) && !fieldNames.has(mapping.placeholderKey) && mapping.placeholderKey !== 'items_table' ) .map((mapping) => ({ placeholderKey: mapping.placeholderKey, sourcePath: mapping.sourcePath })); const tableMapping = mappings.find((mapping) => mapping.placeholderKey === 'items_table') ?? null; const tableColumns = tableMapping ? columns .filter((column) => column.mappingId === tableMapping.id) .map((column) => ({ columnName: column.columnName, sourceField: column.sourceField })) : []; const status = summarizeIssues([ unmappedFields.length ? 'FAIL' : 'PASS', orphanMappings.length ? 'WARNING' : 'PASS' ]); results.push({ templateName: template.templateName, version: template.version, organizationId: template.organizationId, tokenCount: schemaTokens.length, coverage, unmappedFields, orphanMappings, tableColumns, status }); } const overallStatus = summarizeIssues( results.map((result) => result.status) as Array<'PASS' | 'WARNING' | 'FAIL'> ); console.log( JSON.stringify( { audit: 'mapping-coverage', overallStatus, results }, null, 2 ) ); } finally { await sql.end({ timeout: 5 }); } } main().catch((error) => { console.error(`[audit-pdf-mapping-coverage] ${error.message}`); process.exit(1); });