#!/usr/bin/env node /* eslint-disable no-console */ import { createHash } from "node:crypto"; import { mkdir, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { spawn } from "node:child_process"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(__dirname, ".."); const reportPath = path.join(repoRoot, ".tc/baselines/api-auth-surface/baseline-health.md"); const authCentralResolverFile = "apps/web/app/utils/services/apiService.ts"; export const gates = [ { id: "typecheck", command: "pnpm", args: ["run", "typecheck"] }, { id: "lint", command: "pnpm", args: ["run", "lint"] }, { id: "vitest-app", command: "pnpm", args: ["exec", "vitest", "run", "apps/web/app/"] }, { id: "api-auth-surface", command: "node", args: ["scripts/verify-api-auth-surface.mjs", "--json"] }, ]; function shellCommand({ command, args }) { return [command, ...args].join(" "); } function outputFingerprint(output) { const normalized = output .split(/\r?\n/) .map((line) => line.trim()) .filter(Boolean) .slice(-40) .join("\n"); const hash = createHash("sha256").update(output).digest("hex").slice(0, 12); return { hash, excerpt: normalized.slice(0, 900) || "(no output)", }; } function parseNumber(value) { const parsed = Number.parseInt(value, 10); return Number.isFinite(parsed) ? parsed : 0; } function countTypeScriptFailures(output) { return (output.match(/error TS\d+/g) ?? []).length; } function countEslintFailures(output) { const problemsMatch = output.match(/✖\s+(\d+)\s+problems?\s+\((\d+)\s+errors?/i); if (problemsMatch) return parseNumber(problemsMatch[2]); const compactMatch = output.match(/(\d+)\s+errors?/i); if (compactMatch) return parseNumber(compactMatch[1]); return 0; } function countVitestFailures(output) { const failedTestMatch = output.match(/Tests\s+\d+\s+failed/i); if (failedTestMatch) { const countMatch = failedTestMatch[0].match(/Tests\s+(\d+)\s+failed/i); return countMatch ? parseNumber(countMatch[1]) : 1; } const failedFilesMatch = output.match(/Test Files\s+(\d+)\s+failed/i); return failedFilesMatch ? parseNumber(failedFilesMatch[1]) : 0; } function countAuthSurfaceFailures(stdout, output) { try { const inventory = JSON.parse(stdout); return (inventory.directValidateApiKeyCallsites ?? []).filter((entry) => entry.file !== authCentralResolverFile).length; } catch { return output.trim() ? 1 : 0; } } export function failureCount(gateId, stdout, stderr) { const output = `${stdout}\n${stderr}`; if (gateId === "typecheck") return countTypeScriptFailures(output); if (gateId === "lint") return countEslintFailures(output); if (gateId === "vitest-app") return countVitestFailures(output); if (gateId === "api-auth-surface") return countAuthSurfaceFailures(stdout, output); return 0; } function runGate(gate) { return new Promise((resolve) => { const startedAt = Date.now(); const child = spawn(gate.command, gate.args, { cwd: repoRoot, env: { ...process.env, CI: "1" }, shell: false, }); let stdout = ""; let stderr = ""; child.stdout.on("data", (chunk) => { stdout += chunk; }); child.stderr.on("data", (chunk) => { stderr += chunk; }); child.on("error", (error) => { stderr += `\n${error.message}`; }); child.on("close", (code) => { const output = `${stdout}\n${stderr}`; resolve({ id: gate.id, command: shellCommand(gate), exitCode: code ?? 1, durationMs: Date.now() - startedAt, failureCount: failureCount(gate.id, stdout, stderr), fingerprint: outputFingerprint(output), }); }); }); } export function renderReport(results) { const lines = [ "# API Auth Surface Baseline Health", "", `Generated: ${new Date().toISOString()}`, "", "This report captures non-E2E baseline gates for API Auth Surface baseline. The runner exits 0 when every gate was executed and this report was written, even if individual gates fail.", "", ]; for (const result of results) { lines.push( `## ${result.id}`, "", `- Command: \`${result.command}\``, `- Exitcode: ${result.exitCode}`, `- Failure Count: ${result.failureCount}`, `- Duration: ${result.durationMs}ms`, `- Output Fingerprint: ${result.fingerprint.hash}`, "", "```text", result.fingerprint.excerpt, "```", "", ); } return `${lines.join("\n")}\n`; } async function main() { const results = []; for (const gate of gates) { console.log(`Running ${gate.id}: ${shellCommand(gate)}`); results.push(await runGate(gate)); } await mkdir(path.dirname(reportPath), { recursive: true }); await writeFile(reportPath, renderReport(results), "utf8"); console.log(`Wrote ${path.relative(repoRoot, reportPath)}`); } if (process.argv[1] === fileURLToPath(import.meta.url)) { main().catch(async (error) => { console.error(error); try { await mkdir(path.dirname(reportPath), { recursive: true }); await writeFile(reportPath, `# API Auth Surface Baseline Health\n\nRunner failed before report completion.\n\n\`\`\`text\n${error?.stack ?? error}\n\`\`\`\n`, "utf8"); } finally { process.exit(1); } }); }