import { ApiClient, type InspectionResult } from "../api-client.js"; import { t } from "../i18n/index.js"; const POLL_INTERVAL = 2_000; // 2 seconds const MAX_POLLS = 60; // 120 seconds total export async function inspectPage( client: ApiClient, url: string, lang: "en" | "ko", standard?: string, ): Promise { // 1. Start inspection const { inspection_id } = await client.startInspection(url, standard); // 2. Poll until completion let result: InspectionResult | null = null; for (let i = 0; i < MAX_POLLS; i++) { await sleep(POLL_INTERVAL); const data = await client.getInspection(inspection_id); if (data.status === "completed") { result = data; break; } if (data.status === "error") { throw new Error(`Inspection failed for ${url}`); } } // 3. Timeout — return partial info if (!result) { return t("inspect_page.timeout", lang, { url, inspectionId: inspection_id }); } // 4. Format result as markdown return formatResult(result, lang); } function formatResult(r: InspectionResult, lang: "en" | "ko"): string { const lines: string[] = []; const duration = r.duration_seconds ? `${r.duration_seconds.toFixed(1)}s` : "—"; lines.push(`# ${t("result.title", lang)}`); lines.push(`**URL**: ${r.url}`); lines.push( `**${t("result.overall_score", lang)}**: ${r.overall_score}/100 (${r.grade})`, ); lines.push(`**${t("result.duration", lang)}**: ${duration}`); if (r.accessibility_standard) { lines.push(`**${t("result.standard", lang)}**: ${r.accessibility_standard}`); } lines.push(""); // Category scores table lines.push(`## ${t("result.category_scores", lang)}`); lines.push( `| ${t("result.category", lang)} | ${t("result.score", lang)} | ${t("result.grade", lang)} | ${t("result.issues", lang)} |`, ); lines.push("|---|---|---|---|"); const catNames: Array<[string, string]> = [ ["html_css", "HTML/CSS"], ["accessibility", "Accessibility"], ["seo", "SEO"], ["performance_security", "Performance/Security"], ]; for (const [key, label] of catNames) { const cat = r.categories[key as keyof typeof r.categories]; if (!cat) continue; const issueStr = `${cat.total_issues} (C:${cat.critical} M:${cat.major} m:${cat.minor} i:${cat.info})`; lines.push(`| ${label} | ${cat.score} | ${cat.grade} | ${issueStr} |`); } lines.push(""); // Issue summary const s = r.summary; lines.push(`## ${t("result.issue_summary", lang)}`); lines.push( `**${t("result.total", lang)}**: ${s.total_issues} (Critical: ${s.critical}, Major: ${s.major}, Minor: ${s.minor}, Info: ${s.info})`, ); lines.push(""); // Top issues (critical + major, max 5) const topIssues = collectTopIssues(r, 5); if (topIssues.length > 0) { lines.push(`## ${t("result.top_issues", lang)}`); for (const issue of topIssues) { const sevLabel = issue.severity.toUpperCase(); lines.push(`### [${sevLabel}] ${issue.code}`); lines.push(`- **${t("result.message", lang)}**: ${issue.message}`); if (issue.element) { lines.push(`- **${t("result.element", lang)}**: \`${issue.element}\``); } lines.push(`- **${t("result.suggestion", lang)}**: ${issue.suggestion}`); lines.push(""); } } // Hint for more issues lines.push( `> ${t("result.more_issues_hint", lang, { inspectionId: r.inspection_id })}`, ); return lines.join("\n"); } function collectTopIssues(r: InspectionResult, max: number) { const all = Object.values(r.categories).flatMap((cat) => cat.issues); return all .filter((i) => i.severity === "critical" || i.severity === "major") .slice(0, max); } function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); }