index.ts
1import { setupMessageHandler } from "./messages";2import { setupAutoLock } from "./alarms";3import { getSettings, isUnlocked, lock } from "./vault";4import { isRecoveryConfirmPending } from "./recovery-pending";5import { denyAllPendingPasskeyRequests } from "./passkey";67setupMessageHandler();8void setupAutoLock();910// Lock-on-close via port disconnect. chrome.runtime.sendMessage in `pagehide`11// is unreliable in MV3 — the popup context tears down before the message lands.12// A persistent port survives the unload; the SW locks when it sees the disconnect.13chrome.runtime.onConnect.addListener((port) => {14 if (port.name !== "popup") return;15 port.onDisconnect.addListener(async () => {16 // Pending passkey approvals must resolve as denied regardless of lock17 // settings — leaving them stuck blocks the page's navigator.credentials18 // promise for the full 60s timeout (M8/COR-009).19 await denyAllPendingPasskeyRequests();20 if (!isUnlocked() || getSettings().lockOnClose === false) return;21 // Skip lock while the user is mid-recovery-confirmation; locking here22 // would discard the in-memory key needed to re-derive the recovery23 // words on the next popup open, permanently losing them.24 if (await isRecoveryConfirmPending()) return;25 lock();26 });27});