返回 DeepSeek-Reasonix
globalCrashHandlers.ts
根目录 / desktop / frontend / src / lib / globalCrashHandlers.ts
1 // Window-level failure routing. The desktop bridge rejects a bound Go call with the bare
2 // error string, so a stackless rejection is an ordinary backend error that is
3 // contained and toasted; only faults carrying a stack reach the crash overlay.
4
5 import {
6 buildCrashPayload,
7 globalCrashReportReason,
8 isOpaqueScriptErrorEvent,
9 normalizeCrashError,
10 opaqueScriptFingerprintHint,
11 paintCrashOverlay,
12 reportCrash,
13 shouldReportGlobalCrashEvent,
14 } from "./crash";
15 import { recordFrontendDiagnostic } from "./frontendDiagnosticBridge";
16
17 export const RECOVERABLE_ERROR_EVENT = "reasonix:recoverable-error";
18
19 export type RecoverableErrorDetail = { message: string };
20
21 export function isRecoverableRejectionReason(reason: unknown): boolean {
22 if (typeof reason !== "object" || reason === null) return true;
23 const stack = (reason as { stack?: unknown }).stack;
24 return typeof stack !== "string" || stack.trim() === "";
25 }
26
27 export function onRecoverableError(cb: (detail: RecoverableErrorDetail) => void): () => void {
28 const handler = (e: Event) => cb((e as CustomEvent<RecoverableErrorDetail>).detail);
29 window.addEventListener(RECOVERABLE_ERROR_EVENT, handler);
30 return () => window.removeEventListener(RECOVERABLE_ERROR_EVENT, handler);
31 }
32
33 function containRecoverableRejection(e: PromiseRejectionEvent): boolean {
34 if (!isRecoverableRejectionReason(e.reason)) return false;
35 e.preventDefault();
36 recordFrontendDiagnostic("runtime", "unhandled-backend-rejection", { status: "error" });
37 const detail: RecoverableErrorDetail = { message: normalizeCrashError(e.reason).errorMessage };
38 window.dispatchEvent(new CustomEvent(RECOVERABLE_ERROR_EVENT, { detail }));
39 return true;
40 }
41
42 export function installGlobalCrashHandlers() {
43 window.addEventListener("error", (e) => {
44 if (!shouldReportGlobalCrashEvent(e)) return;
45 const payload = buildCrashPayload("window.error", globalCrashReportReason(e));
46 if (isOpaqueScriptErrorEvent(e)) payload.fingerprintHint = opaqueScriptFingerprintHint();
47 paintCrashOverlay(payload);
48 });
49 window.addEventListener("unhandledrejection", (e) => {
50 if (!shouldReportGlobalCrashEvent(e) || containRecoverableRejection(e)) return;
51 reportCrash("unhandledrejection", e.reason);
52 });
53 }
54
54 lines TYPESCRIPT