Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124/** * `corpora verify` — the command you run while nervous about a cutover. * * The output is built to be read under stress: one line per check, aligned, grouped the way the * resolution chain is ordered, failures repeated at the end with the detail that tells you what to * do. `--json` for machines. Non-zero exit on any failure. * * @see [INTERFACES](../../docs/INTERFACES.md) */
import { parseArgs } from 'node:util'
import { verifyAuthority } from './index.js'
const USAGE = `corpora verify — check a built or deployed authority over HTTP
Usage: corpora verify --origin <url> [--did <did>] [--source <dir>] [--json]
Options: --origin <url> Base URL of the authority. Required. --did <did> The DID the authority must claim. Read from did.json otherwise. --source <dir> Lexicon source directory; served records are diffed against it. --json Machine-readable report on stdout.
Also accepted: --expect-did-doc <hash> Expected sha256 of did.json bytes, as published alongside the source. --no-dns Skip the _lexicon TXT checks. Use before DNS is flipped to a new DID. --dns-server <addr> Ask this resolver instead of the system one. Repeatable. Also read from CORPORA_DNS_SERVERS, comma separated.
Exits non-zero if any check fails.`
/** * @param {string[]} argv * @returns {Promise<number>} */export async function run(argv) { if (argv.includes('--help') || argv.includes('-h')) { process.stdout.write(USAGE) return 0 }
const { values } = parseArgs({ args: argv, options: { origin: { type: 'string' }, did: { type: 'string' }, source: { type: 'string' }, json: { type: 'boolean' }, 'expect-did-doc': { type: 'string' }, 'no-dns': { type: 'boolean' }, 'dns-server': { type: 'string', multiple: true }, }, })
if (!values.origin) { process.stderr.write(`corpora verify: --origin is required\n\n${USAGE}`) return 1 }
const envServers = (process.env.CORPORA_DNS_SERVERS ?? '').split(',').map((s) => s.trim()).filter(Boolean) const dnsServers = values['dns-server'] ?? (envServers.length ? envServers : undefined)
const report = await verifyAuthority({ origin: values.origin, did: values.did, sourceDir: values.source, expectDidDoc: values['expect-did-doc'], skipDns: values['no-dns'], dnsServers, })
if (values.json) { process.stdout.write(`${JSON.stringify(report, null, 2)}\n`) } else { process.stdout.write(format(report)) } return report.ok ? 0 : 1}
/** * @param {import('./index.js').VerifyReport} report * @returns {string} */export function format(report) { const colour = process.stdout.isTTY && !process.env.NO_COLOR const dim = (/** @type {string} */ s) => (colour ? `\x1b[2m${s}\x1b[0m` : s) const red = (/** @type {string} */ s) => (colour ? `\x1b[31m${s}\x1b[0m` : s) const green = (/** @type {string} */ s) => (colour ? `\x1b[32m${s}\x1b[0m` : s) const bold = (/** @type {string} */ s) => (colour ? `\x1b[1m${s}\x1b[0m` : s)
const width = Math.max(0, ...report.checks.map((c) => c.name.length)) const lines = [``, `${bold('corpora verify')} ${report.origin ?? ''}`]
/** @type {string | undefined} */ let group for (const check of report.checks) { if (check.group !== group) { group = check.group lines.push(``, ` ${dim(group ?? '')}`) } const mark = check.ok ? green('ok ') : red('FAIL') lines.push(` ${mark} ${check.name.padEnd(width)} ${dim(check.detail)}`) }
const failed = report.checks.filter((c) => !c.ok) const count = `${report.checks.length} check${report.checks.length === 1 ? '' : 's'}` const scope = report.nsids.length ? `${report.nsids.length} lexicon${report.nsids.length === 1 ? '' : 's'} under ${report.did}` : (report.did ?? '')
lines.push(``) if (failed.length === 0) { lines.push(` ${green('passed')} ${count}${scope ? `, ${scope}` : ''}`) } else { lines.push(` ${red(`${failed.length} of ${count} failed`)}${scope ? `, ${scope}` : ''}`) for (const check of failed) lines.push(` ${red('·')} ${check.name}: ${check.detail}`) } lines.push(``) return lines.join('\n')}