返回 DeepSeek-Reasonix
firebase_crash_view.ts
根目录 / workers / crash-report / src / firebase_crash_view.ts
1 import type { Env } from "./env";
2 import type { ReportSample } from "./group";
3 import type {
4 FirebaseCrashGroupMeta,
5 FirebaseCrashSample,
6 } from "./firebase_rtdb";
7
8 export type FirebaseGroupRow = {
9 fingerprint: string;
10 kind: string;
11 count: number;
12 first_seen: string;
13 last_seen: string;
14 first_version: string;
15 last_version: string;
16 status: string;
17 title: string;
18 source: string;
19 label: string;
20 error_type: string;
21 top_frame: string;
22 severity: string;
23 last_os: string;
24 last_arch: string;
25 last_build_commit: string;
26 last_channel: string;
27 regressed_at: string;
28 regression_review: string;
29 resolution_platform: string;
30 resolution_runtime: string;
31 resolution_basis: string;
32 last_category: string;
33 };
34
35 export async function loadFirebaseGroupMeta(
36 env: Env,
37 fingerprint: string,
38 ): Promise<FirebaseGroupRow | null> {
39 return env.DB.prepare(
40 `SELECT fingerprint, kind, count, first_seen, last_seen, first_version, last_version,
41 status, title, source, label, error_type, top_frame, severity, last_os, last_arch,
42 last_build_commit, last_channel, regressed_at, regression_review,
43 resolution_platform, resolution_runtime, resolution_basis, last_category
44 FROM groups WHERE fingerprint = ?1`,
45 ).bind(fingerprint).first<FirebaseGroupRow>();
46 }
47
48 export function firebaseMeta(group: FirebaseGroupRow): FirebaseCrashGroupMeta {
49 return {
50 fingerprint: group.fingerprint,
51 kind: group.kind,
52 count: Number(group.count),
53 firstSeen: group.first_seen,
54 lastSeen: group.last_seen,
55 firstVersion: group.first_version,
56 lastVersion: group.last_version,
57 status: group.status,
58 title: group.title,
59 source: group.source,
60 label: group.label,
61 errorType: group.error_type,
62 topFrame: group.top_frame,
63 severity: group.severity,
64 lastOS: group.last_os,
65 lastArch: group.last_arch,
66 lastBuildCommit: group.last_build_commit,
67 lastChannel: group.last_channel,
68 regressedAt: group.regressed_at,
69 regressionReview: group.regression_review,
70 resolutionPlatform: group.resolution_platform,
71 resolutionRuntime: group.resolution_runtime,
72 resolutionBasis: group.resolution_basis,
73 lastCategory: group.last_category,
74 writerGeneration: 0,
75 sampleEpoch: 1,
76 sampleState: "active",
77 };
78 }
79
80 export function firebaseSamples(
81 samples?: {
82 first?: FirebaseCrashSample;
83 latest?: Record<string, FirebaseCrashSample | { marker?: unknown }> |
84 Array<FirebaseCrashSample | { marker?: unknown }>;
85 },
86 ): ReportSample[] {
87 if (!samples) return [];
88 const isSample = (value: FirebaseCrashSample | { marker?: unknown }): value is FirebaseCrashSample =>
89 typeof (value as Partial<FirebaseCrashSample>).eventId === "string";
90 const latest = Array.isArray(samples.latest)
91 ? samples.latest.filter(isSample)
92 : Object.values(samples.latest ?? {}).filter(isSample);
93 const unique = new Map<string, FirebaseCrashSample>();
94 for (const sample of [...latest, ...(samples.first ? [samples.first] : [])]) {
95 if (sample && typeof sample.eventId === "string") unique.set(sample.eventId, sample);
96 }
97 return [...unique.values()]
98 .sort((left, right) => String(right.receivedAt).localeCompare(String(left.receivedAt)))
99 .map(firebaseSampleToReport);
100 }
101
102 function firebaseSampleToReport(sample: FirebaseCrashSample): ReportSample {
103 const string = (key: string) => typeof sample[key] === "string" ? sample[key] as string : "";
104 const json = (key: string, fallback: unknown) => JSON.stringify(sample[key] ?? fallback);
105 const diagnostics = sample.diagnostics && typeof sample.diagnostics === "object" && !Array.isArray(sample.diagnostics)
106 ? sample.diagnostics as Record<string, unknown>
107 : undefined;
108 const diagnosticString = (key: string) => typeof diagnostics?.[key] === "string" ? diagnostics[key] as string : "";
109 return {
110 version: diagnosticString("subjectVersion") || string("version"),
111 os: string("os"),
112 arch: string("arch"),
113 message: string("message"),
114 device: json("device", {}),
115 created_at: string("receivedAt"),
116 source: string("source"),
117 label: string("label"),
118 error_type: string("errorType"),
119 error_message: string("errorMessage"),
120 error_family: string("errorFamily"),
121 top_frame: string("topFrame"),
122 build_commit: diagnosticString("subjectBuildCommit") || string("buildCommit"),
123 channel: diagnosticString("subjectChannel") || string("channel"),
124 language: string("language"),
125 view: string("view"),
126 breadcrumbs: json("breadcrumbs", []),
127 component_stack: string("componentStack"),
128 stack: string("stack"),
129 occurred_at: string("occurredAt"),
130 webview2: sample.webview2 ? json("webview2", {}) : "",
131 web_runtime: sample.webRuntime ? json("webRuntime", {}) : "",
132 event_id: sample.eventId,
133 incident_id: diagnosticString("incidentId"),
134 diagnostics: diagnostics ? JSON.stringify(diagnostics) : "",
135 };
136 }
137
137 lines TYPESCRIPT