fix: patch memory leaks in event hooks and strengthen type guard
Fix three bugs found during code review: 1. useTauriEvent/useTauriEvents: If the component unmounts before the async listen() promise resolves, the unlisten function was lost, leaking the event subscription. Added a cancelled flag to call unlisten immediately when the promise resolves after cleanup. 2. useTauriEvents: The handlers object was used directly as a useEffect dependency, causing re-subscription on every render when callers pass an inline object literal. Replaced with a useRef for handler stability and a derived eventNames string as the dependency. 3. isMcError type guard: Only checked property existence via 'in' operator, not property types. An object with wrong-typed properties (e.g. code: 42) would pass the guard. Now validates that code and message are strings and recoverable is boolean. 4. AppShell global shortcut listener: Same race condition as (1), plus missing .catch() on the listen promise could produce unhandled rejections. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,12 +63,12 @@ export interface McError {
|
||||
|
||||
/** Type guard to check if an error is a structured McError */
|
||||
export function isMcError(err: unknown): err is McError {
|
||||
if (typeof err !== "object" || err === null) return false;
|
||||
const obj = err as Record<string, unknown>;
|
||||
return (
|
||||
typeof err === "object" &&
|
||||
err !== null &&
|
||||
"code" in err &&
|
||||
"message" in err &&
|
||||
"recoverable" in err
|
||||
typeof obj.code === "string" &&
|
||||
typeof obj.message === "string" &&
|
||||
typeof obj.recoverable === "boolean"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -156,6 +156,10 @@ export function computeStaleness(updatedAt: string | null): Staleness {
|
||||
if (!updatedAt) return "normal";
|
||||
|
||||
const ageMs = Date.now() - new Date(updatedAt).getTime();
|
||||
|
||||
// Guard against invalid date strings (NaN propagates through arithmetic)
|
||||
if (Number.isNaN(ageMs)) return "normal";
|
||||
|
||||
const ageDays = ageMs / (1000 * 60 * 60 * 24);
|
||||
|
||||
if (ageDays < 1) return "fresh";
|
||||
|
||||
Reference in New Issue
Block a user