/** * types.ts — cross-boundary types and the `MessageType` union. * * `MessageType` mixes background-bound and content-script-bound messages. * Background-bound types have a case in `src/background/messages.ts`. * Content-script-bound types (PING, SUMMON_AUTOFILL, VAULT_LOCKED, * RESTART_AUTO_LOCK, etc.) are sent *to* content scripts or the popup and * have no handler in messages.ts by design — that absence is not a missing * handler. Add new cross-boundary types here, not inline in call sites. */ export interface ManualSummonRecord { firstSummonedAt: number; summonCount: number; lastSummonedAt: number; } export interface Canary { webhookUrl: string; label?: string; lastPinged?: number; pingCount?: number; } export type CardNetwork = "visa" | "mastercard" | "amex" | "discover" | "unknown"; export interface VaultEntry { id: string; // crypto.randomUUID() type?: "login" | "note" | "passkey" | "card"; // defaults to "login" for existing entries title: string; username?: string; // only for login entries password?: string; // only for login entries url?: string; // only for login entries totp?: string; // base32 seed, stored encrypted — only for login entries notes?: string; breach?: number; // HIBP exposure count; 0 or undefined = clean — only for login entries canary?: Canary; createdAt: number; // Date.now() modifiedAt: number; deletedAt?: number; tags?: string[]; rpId?: string; rpName?: string; credentialId?: string; publicKeyCose?: string; privateKeyJwk?: JsonWebKey; userHandle?: string; userName?: string; userDisplayName?: string; algorithm?: -7 | -8; signCount?: number; aaguid?: string; backupEligible?: boolean; backupState?: boolean; lastUsedAt?: number; // Card entry fields cardholderName?: string; cardNumber?: string; // full PAN, stored encrypted in vault expMonth?: string; expYear?: string; cvc?: string; cardNickname?: string; network?: CardNetwork; last4?: string; } export interface LegacyVaultEntry { id: string; title: string; username: string; password: string; url: string; totp?: string; notes?: string; breach?: number; created: number; updated: number; deletedAt?: number; } export interface CardEntry extends VaultEntry { type: "card"; cardholderName: string; cardNumber: string; expMonth: string; expYear: string; cvc: string; } export interface PasskeyEntry extends VaultEntry { type: "passkey"; rpId: string; rpName: string; credentialId: string; publicKeyCose: string; privateKeyJwk: JsonWebKey; userHandle: string; userName: string; userDisplayName: string; algorithm: -7 | -8; signCount: number; aaguid: string; backupEligible: boolean; backupState: boolean; lastUsedAt?: number; } export interface PendingPasskeyRequest { requestId: number; type: "create" | "get"; rpId: string; rpName: string; userName: string; userDisplayName?: string; } export interface PasskeyProviderState { supported: boolean; enabled: boolean; attached: boolean; browserLabel: string; statusLabel: string; conflict?: string; } export interface StoredVault { version: 1; // AAD for AES-GCM --- prevents downgrade salt: string; // hex --- 16 bytes, CSPRNG, stored not secret nonce: string; // hex --- 12 bytes, CSPRNG, fresh per write ciphertext: string; // hex --- AES-256-GCM output tag: string; // hex --- 16-byte GCM auth tag } // Background-bound message types — sent from popup/content scripts, // handled in src/background/messages.ts. Everything in this group must // have a matching case in that file's switch statement. export type MessageType = | "UNLOCK" | "LOCK" | "IS_UNLOCKED" | "HAS_VAULT" | "GET_ENTRIES" | "ADD_ENTRY" | "BATCH_ADD_ENTRIES" | "RESTORE_ENTRIES" | "CLEAR_VAULT_ENTRIES" | "UPDATE_ENTRY" | "DELETE_ENTRY" | "AUTOFILL_QUERY" | "COPY" | "CREATE_VAULT" | "GET_ENTRY_FOR_FILL" | "CHECK_BREACH" | "CHECK_ALL_BREACHES" | "GET_SETTINGS" | "UPDATE_SETTINGS" | "FIRE_CANARY_PING" | "ACTIVATE_LICENSE" | "GET_LICENSE" | "IS_DURESS_ENABLED" | "SETUP_DURESS" | "DISABLE_DURESS" | "VERIFY_MAIN_PASSWORD" | "CHANGE_PASSWORD" | "GET_SITE_PASSWORD_POLICY" | "GET_SYNC_STATE" | "TRIGGER_SYNC" | "START_PAIRING" | "CANCEL_PAIRING" | "ACCEPT_PAIRING" | "REMOVE_PAIRED_DEVICE" | "REMOVE_TRUSTED_NETWORK" | "MARK_NETWORK_TRUSTED" | "GET_PENDING_PASSKEY" | "APPROVE_PASSKEY" | "DENY_PASSKEY" | "DELETE_PASSKEY" | "GET_PASSKEYS" | "GET_PASSKEY_PROVIDER_STATE" | "SET_PASSKEY_PROVIDER_ENABLED" | "GET_DELETED_ENTRIES" | "RESTORE_ENTRY" | "PURGE_ENTRY" | "PURGE_EXPIRED_TRASH" | "LIST_VAULT_SNAPSHOTS" | "RESTORE_VAULT_SNAPSHOT" | "COUNT_DUPLICATES" | "DEDUPLICATE_ENTRIES" | "RECORD_MANUAL_SUMMON" | "DECLINE_CARD_SAVE" | "IS_CARD_DECLINED" | "GET_VAULT_STATUS" // Content-script-bound message types — sent BY the background TO content // scripts. They never reach messages.ts's switch. Kept in this union so // existing call sites type-check; do not add cases for them in the // background handler. | "PING" // background → content: liveness probe | "SUMMON_AUTOFILL" // background → content: open the in-page palette | "VAULT_LOCKED" // background → content/popup: vault just locked | "VAULT_UNLOCKED"; // background → content/popup: vault just unlocked export const ACCENT_COLORS = [ { name: "peach", hex: "#E8956D" }, // brand { name: "rose", hex: "#F08296" }, { name: "plum", hex: "#C58BE6" }, { name: "lavender", hex: "#9C9EF0" }, { name: "sky", hex: "#6EBCDE" }, { name: "mint", hex: "#6FD4AC" }, { name: "sage", hex: "#A8C870" }, { name: "butter", hex: "#F0CC60" }, { name: "sand", hex: "#DCB280" }, { name: "clay", hex: "#E0825E" }, { name: "slate", hex: "#88AEC8" }, { name: "stone", hex: "#B8A89C" }, ] as const; export type AccentColorName = typeof ACCENT_COLORS[number]["name"]; export interface PasswordPolicy { minLength?: number; maxLength?: number; requiresUppercase?: boolean; requiresLowercase?: boolean; requiresNumbers?: boolean; requiresSymbols?: boolean; forbidsSymbols?: boolean; allowedSymbols?: string; /** Characters the site explicitly disallows. Filtered from every pool before generation. */ forbiddenChars?: string; } export interface Message { type: MessageType; payload?: unknown; } export interface MessageResponse { ok: boolean; data?: unknown; error?: string; unlocked?: boolean; }