import { setupMessageHandler } from "./messages"; import { setupAutoLock } from "./alarms"; import { ensureSettingsLoaded, isUnlocked, lock } from "./vault"; import { isRecoveryConfirmPending } from "./recovery-pending"; import { denyAllPendingPasskeyRequests } from "./passkey"; setupMessageHandler(); void setupAutoLock(); // Lock-on-close via port disconnect. chrome.runtime.sendMessage in `pagehide` // is unreliable in MV3 — the popup context tears down before the message lands. // A persistent port survives the unload; the SW locks when it sees the disconnect. chrome.runtime.onConnect.addListener((port) => { if (port.name !== "popup") return; port.onDisconnect.addListener(async () => { // Pending passkey approvals must resolve as denied regardless of lock // settings — leaving them stuck blocks the page's navigator.credentials // promise for the full 60s timeout (M8/COR-009). await denyAllPendingPasskeyRequests(); const s = await ensureSettingsLoaded(); if (!isUnlocked() || s.lockOnClose === false) return; // Skip lock while the user is mid-recovery-confirmation; locking here // would discard the in-memory key needed to re-derive the recovery // words on the next popup open, permanently losing them. if (await isRecoveryConfirmPending()) return; lock(); }); });