返回 DeepSeek-Reasonix
stats_reports.ts
根目录 / workers / crash-report / src / stats_reports.ts
1 import { esc } from "./shell";
2
3 export function i18n(en: string, zh: string): string {
4 return `<span data-i18n="en">${esc(en)}</span><span data-i18n="zh">${esc(zh)}</span>`;
5 }
6
7 export function i18nHTML(en: string, zh: string): string {
8 return `<span data-i18n="en">${en}</span><span data-i18n="zh">${zh}</span>`;
9 }
10
11 export type CrashRow = {
12 fingerprint: string;
13 kind: string;
14 count: number;
15 first_version: string;
16 last_version: string;
17 seen: string;
18 status: string;
19 title: string;
20 source: string;
21 label: string;
22 error_type: string;
23 top_frame: string;
24 severity: string;
25 last_os: string;
26 last_arch: string;
27 last_channel: string;
28 regressed_at: string;
29 last_category?: string;
30 development?: boolean;
31 affected_installs?: number;
32 window_events?: number;
33 identified_events?: number;
34 identity_coverage?: number;
35 dimension_coverage?: number;
36 impact_rate?: number | null;
37 };
38
39 export function clip(s: string, n: number): string {
40 return s.length > n ? `${s.slice(0, n - 1)}…` : s;
41 }
42
43 export function statusPill(status: string): string {
44 if (status === "resolved") return `<span class="pill status-resolved">${i18n("Resolved", "已解决")}</span>`;
45 if (status === "ignored") return `<span class="pill status-ignored">${i18n("Ignored", "已忽略")}</span>`;
46 return `<span class="pill status-open open">${i18n("Open", "未处理")}</span>`;
47 }
48
49 function crashMetric(en: string, zh: string, value: string | number, className = ""): string {
50 return `<span class="crash-metric${className ? ` ${className}` : ""}"><b>${esc(value)}</b><small>${i18n(en, zh)}</small></span>`;
51 }
52
53 export function reportGroups(rows: CrashRow[], compact = false, windowDays: 7 | 30 = 30): string {
54 if (!rows.length) return `<div class="empty">${i18n("No diagnostic reports yet — that's the good kind of empty", "还没有诊断报告,这是好消息")}</div>`;
55 return `<div class="crash-list${compact ? " compact" : ""}"><div class="crash-head"><span>${i18n("summary", "摘要")}</span><span>${i18n("scope", "范围")}</span><span>${i18n("health", "状态")}</span><span title="${i18n("Window events, impact rate, identity coverage, and lifetime count", "窗口事件、影响率、身份覆盖率和累计事件")}">${i18n("triage metrics", "分诊指标")}</span></div>${rows
56 .map((c) => {
57 const platform = [c.last_os, c.last_arch].filter(Boolean).join("/");
58 const versions = `${c.first_version || "?"} → ${c.last_version || "?"}`;
59 const title = c.title || c.error_type || c.top_frame || c.fingerprint;
60 const identityCoverage = Number(c.identity_coverage ?? 0) >= 0.9 && Number(c.dimension_coverage ?? 1) >= 0.9;
61 const impactRate = c.impact_rate !== null && c.impact_rate !== undefined ? `${(Number(c.impact_rate) * 100).toFixed(1)}%` : "—";
62 return `<a class="crash-item" href="/stats/group/${esc(c.fingerprint)}" title="${esc(title)}">
63 <span class="crash-summary" aria-label="${esc(title)}"><span>${c.title ? esc(clip(c.title, compact ? 150 : 180)) : `<span class="muted">${i18n("No summary captured", "暂无摘要")}</span>`}</span><small>${esc(c.fingerprint.slice(0, 8))} · ${esc(c.seen)}</small>${
64 c.regressed_at ? `<em>${i18nHTML(`regressed ${esc(c.regressed_at.slice(0, 10))}`, `回归 ${esc(c.regressed_at.slice(0, 10))}`)}</em>` : ""
65 }</span>
66 <span class="crash-scope"><span class="crash-meta"><b>${i18n("Source", "来源")}</b><span>${esc(c.source || "legacy")}</span></span><span class="crash-meta"><b>${i18n("Versions", "版本")}</b><span>${esc(versions)}</span></span><span class="crash-meta"><b>${i18n("Platform", "平台")}</b><span>${platform ? esc(platform) : "unknown platform"}</span></span>${c.last_channel && c.last_channel !== "stable" ? `<span class="crash-meta"><b>${i18n("Channel", "渠道")}</b><span>${esc(c.last_channel)}</span></span>` : ""}</span>
67 <span class="crash-health"><span class="pill">${esc(c.severity || "medium")}</span><span class="pill ${c.kind === "crash" ? "crash" : ""}">${esc(c.kind)}</span>${statusPill(c.status)}</span>
68 <span class="crash-count"><span class="crash-metrics">${crashMetric(`Affected installs (${windowDays}d)`, `受影响安装(${windowDays}天)`, Number(c.affected_installs ?? 0), "primary")}${crashMetric("Impact rate", "影响率", impactRate)}${crashMetric("Window events", "窗口事件", Number(c.window_events ?? 0))}${crashMetric("Identity coverage", "身份覆盖率", identityCoverage ? `${Math.round(Number(c.identity_coverage) * 100)}%` : "sample incomplete / 样本不完整")}${crashMetric("Lifetime events", "累计事件", Number(c.count), "full")}</span></span>
69 </a>`;
70 })
71 .join("")}</div>`;
72 }
73
73 lines TYPESCRIPT