// Writes contracts/reason-codes.html from reason-codes.json: the page a refusal's `contract` // field points to (D-136), one anchor per seller code (#) and per cause (#detail-). // The JSON is the contract; this page is a reading of it and is regenerated, never edited. // The contracts test fails when the two disagree. // // node contracts/generate-reason-codes-html.mjs import { readFile, writeFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; const escape = (text) => String(text ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]); const code = (text) => `${escape(text)}`; export function renderReasonCodes(contract) { const seen = new Set(); // The first place a code appears carries its anchor; a code listed under two checks is one address. const anchor = (name) => (seen.has(name) ? '' : (seen.add(name), ` id="${escape(name)}"`)); const row = (name, status, note) => `${code(name)}${escape(status ?? '')}${escape(note)}`; const table = (rows) => `${rows.join('')}
CodeHTTPWhat it means
`; const plain = (section) => Object.entries(section).filter(([k]) => k !== '$comment').map(([name, note]) => row(name, '', note)); const steps = Object.entries(contract.steps).map(([n, step]) => `

Check ${escape(n)}: ${escape(step.name)}

${step.entry ? 'Writes a signed request.refused entry.' : 'Writes no entry; counted.'}

${table(Object.entries(step.codes).map(([name, c]) => row(name, c.status, `${c.legacy ? '(legacy) ' : ''}${c.note ?? ''}`)))}`); const details = Object.entries(contract.details ?? {}).filter(([k]) => k !== '$comment').map(([name, d]) => `${code(name)}${d.code.includes(' ') ? escape(d.code) : `${code(d.code)}`}${escape(d.text)}`); return ` Reason codes

Reason codes

Generated from reason-codes.json, which is the contract. ${escape(contract.$comment)}

Causes

${escape(contract.details?.$comment ?? '')}

${details.join('')}
CauseCodeWhat failed

By check

${steps.join('\n')}

Other seller codes

${table(plain(contract.other_seller_codes))}

Buyer side

${escape(contract.buyer_side.$comment)}

${table(plain(contract.buyer_side))}

Authority side

${escape(contract.authority_side.$comment)}

${table(plain(contract.authority_side))} `; } if (process.argv[1] === fileURLToPath(import.meta.url)) { const contract = JSON.parse(await readFile(new URL('./reason-codes.json', import.meta.url), 'utf8')); await writeFile(new URL('./reason-codes.html', import.meta.url), renderReasonCodes(contract)); }