import { clearSessionToken, getSessionToken, getStoredSession } from '@/background/backend-client'; import { getConnectionStatus, setConnectionStatus } from '@/background/connection-status'; import { applyLockState, getLockState } from '@/background/lock-state'; import { handleWalletConnected, requestAccountSwitch, requestWalletDisconnect, requestWalletReconnect, } from '@/background/wallet-auth'; import { getSettings, updateSettings } from '@/background/settings-client'; import { connectWsClient } from '@/background/ws-client'; import type { NexaMessage, SettingsResult } from '@/shared/messaging'; export default defineBackground(() => { const ws = connectWsClient({ getToken: getSessionToken, onLockState: (state) => void applyLockState(state), onStatusChange: (status) => void setConnectionStatus(status), onAuthExpired: () => { void clearSessionToken().then(() => requestWalletReconnect()); }, }); async function signOut(): Promise { await clearSessionToken(); ws.disconnect(); await requestWalletDisconnect(); } browser.runtime.onMessage.addListener((message: NexaMessage, sender, sendResponse) => { switch (message?.type) { case 'nexa:get-lock-state': getLockState().then(sendResponse); return true; // keep the message channel open for the async response case 'nexa:get-connection-status': sendResponse(getConnectionStatus()); return false; case 'nexa:open-popup': // Best-effort: not all browsers/contexts allow programmatic popup // opening outside a direct user gesture on the action icon. browser.action.openPopup().catch(() => undefined); return false; case 'nexa:wallet-connected': // A content script reports this on every page load (it always tries // a silent onlyIfTrusted connect first), and after an explicit // account switch. Only re-authenticate (which means asking Phantom // to sign a fresh nonce — a popup every time, unlike a silent // connect) when this isn't the wallet we're already signed in as; a // bare "do we have a token" check can't tell those apart. getStoredSession() .then((session) => { if (session?.walletAddress === message.walletAddress) return; return handleWalletConnected(sender.tab?.id, message.walletAddress).then(() => ws.reconnectNow()); }) .catch((err) => console.debug('[nexa/background] wallet auth failed:', err)); // ws-client's own retry loop keeps trying regardless return false; case 'nexa:wallet-disconnected': // Disconnected directly in Phantom's UI (not via our own sign-out // flow, which already clears/disconnects itself) — treat the same way. void clearSessionToken().then(() => ws.disconnect()); return false; case 'nexa:sign-out': void signOut(); return false; case 'nexa:switch-account': void requestAccountSwitch(); return false; case 'nexa:get-settings': getSettings() .then((settings) => sendResponse({ settings } satisfies SettingsResult)) .catch((err) => sendResponse({ error: String(err) } satisfies SettingsResult)); return true; // keep the message channel open for the async response case 'nexa:update-settings': updateSettings(message.settings) .then((settings) => sendResponse({ settings } satisfies SettingsResult)) .catch((err) => sendResponse({ error: String(err) } satisfies SettingsResult)); return true; default: return undefined; } }); });