types.ts

8.4 KB · raw

1/**2 * types.ts — cross-boundary types and the `MessageType` union.3 *4 * `MessageType` mixes background-bound and content-script-bound messages.5 * Background-bound types have a case in `src/background/messages.ts`.6 * Content-script-bound types (PING, SUMMON_AUTOFILL, VAULT_LOCKED,7 * RESTART_AUTO_LOCK, etc.) are sent *to* content scripts or the popup and8 * have no handler in messages.ts by design — that absence is not a missing9 * handler. Add new cross-boundary types here, not inline in call sites.10 */1112export interface ManualSummonRecord {13  firstSummonedAt: number;14  summonCount: number;15  lastSummonedAt: number;16}1718export interface Canary {19  webhookUrl: string;20  label?: string;21  lastPinged?: number;22  pingCount?: number;23}2425export type CardNetwork = "visa" | "mastercard" | "amex" | "discover" | "unknown";2627export interface VaultEntry {28  id: string; // crypto.randomUUID()29  type?: "login" | "note" | "passkey" | "card"; // defaults to "login" for existing entries30  title: string;31  username?: string; // only for login entries32  password?: string; // only for login entries33  url?: string; // only for login entries34  /**35   * Additional sites this entry should match for autofill. Login entries only.36   * Each value is a full origin or URL (e.g. `https://login.foo.com`).37   * The vault domain index keys against the hostname of every entry in this38   * list, in addition to `url`.39   */40  additionalUrls?: string[];41  totp?: string; // base32 seed, stored encrypted — only for login entries42  notes?: string;43  breach?: number; // HIBP exposure count; 0 or undefined = clean — only for login entries44  /**45   * When the password field was last changed. Set on first-write of `password`46   * and refreshed on every subsequent change. Defaults to `createdAt` when47   * legacy entries are read by the advanced health analyser.48   */49  passwordChangedAt?: number;50  canary?: Canary;51  createdAt: number; // Date.now()52  modifiedAt: number;53  deletedAt?: number;54  tags?: string[];55  rpId?: string;56  rpName?: string;57  credentialId?: string;58  publicKeyCose?: string;59  privateKeyJwk?: JsonWebKey;60  userHandle?: string;61  userName?: string;62  userDisplayName?: string;63  algorithm?: -7 | -8;64  signCount?: number;65  aaguid?: string;66  backupEligible?: boolean;67  backupState?: boolean;68  lastUsedAt?: number;69  // Card entry fields70  cardholderName?: string;71  cardNumber?: string;       // full PAN, stored encrypted in vault72  expMonth?: string;73  expYear?: string;74  cvc?: string;75  cardNickname?: string;76  network?: CardNetwork;77  last4?: string;78}7980export interface LegacyVaultEntry {81  id: string;82  title: string;83  username: string;84  password: string;85  url: string;86  totp?: string;87  notes?: string;88  breach?: number;89  created: number;90  updated: number;91  deletedAt?: number;92}9394export interface CardEntry extends VaultEntry {95  type: "card";96  cardholderName: string;97  cardNumber: string;98  expMonth: string;99  expYear: string;100  cvc: string;101}102103export interface PasskeyEntry extends VaultEntry {104  type: "passkey";105  rpId: string;106  rpName: string;107  credentialId: string;108  publicKeyCose: string;109  privateKeyJwk: JsonWebKey;110  userHandle: string;111  userName: string;112  userDisplayName: string;113  algorithm: -7 | -8;114  signCount: number;115  aaguid: string;116  backupEligible: boolean;117  backupState: boolean;118  lastUsedAt?: number;119}120121export interface PendingPasskeyRequest {122  requestId: number;123  type: "create" | "get";124  rpId: string;125  rpName: string;126  userName: string;127  userDisplayName?: string;128}129130export interface PasskeyProviderState {131  supported: boolean;132  enabled: boolean;133  attached: boolean;134  browserLabel: string;135  statusLabel: string;136  conflict?: string;137}138139export interface StoredVault {140  version: 1; // AAD for AES-GCM --- prevents downgrade141  salt: string; // hex --- 16 bytes, CSPRNG, stored not secret142  nonce: string; // hex --- 12 bytes, CSPRNG, fresh per write143  ciphertext: string; // hex --- AES-256-GCM output144  tag: string; // hex --- 16-byte GCM auth tag145}146147// Background-bound message types — sent from popup/content scripts,148// handled in src/background/messages.ts. Everything in this group must149// have a matching case in that file's switch statement.150export type MessageType =151  | "UNLOCK"152  | "LOCK"153  | "IS_UNLOCKED"154  | "HAS_VAULT"155  | "GET_ENTRIES"156  | "ADD_ENTRY"157  | "BATCH_ADD_ENTRIES"158  | "RESTORE_ENTRIES"159  | "CLEAR_VAULT_ENTRIES"160  | "UPDATE_ENTRY"161  | "ADD_ADDITIONAL_URL"162  | "DELETE_ENTRY"163  | "AUTOFILL_QUERY"164  | "COPY"165  | "CREATE_VAULT"166  | "GET_ENTRY_FOR_FILL"167  | "CHECK_BREACH"168  | "CHECK_ALL_BREACHES"169  | "GET_SETTINGS"170  | "UPDATE_SETTINGS"171  | "FIRE_CANARY_PING"172  | "ACTIVATE_LICENSE"173  | "GET_LICENSE"174  | "GET_LICENSE_KEY"175  | "IS_DURESS_ENABLED"176  | "SETUP_DURESS"177  | "DISABLE_DURESS"178  | "VERIFY_MAIN_PASSWORD"179  | "CHANGE_PASSWORD"180  | "GET_SITE_PASSWORD_POLICY"181  | "GET_SYNC_STATE"182  | "TRIGGER_SYNC"183  | "START_PAIRING"184  | "CANCEL_PAIRING"185  | "ACCEPT_PAIRING"186  | "REMOVE_PAIRED_DEVICE"187  | "REMOVE_TRUSTED_NETWORK"188  | "MARK_NETWORK_TRUSTED"189  | "GET_PENDING_PASSKEY"190  | "APPROVE_PASSKEY"191  | "DENY_PASSKEY"192  | "DELETE_PASSKEY"193  | "GET_PASSKEYS"194  | "GET_PASSKEY_PROVIDER_STATE"195  | "SET_PASSKEY_PROVIDER_ENABLED"196  | "GET_DELETED_ENTRIES"197  | "RESTORE_ENTRY"198  | "PURGE_ENTRY"199  | "PURGE_EXPIRED_TRASH"200  | "LIST_VAULT_SNAPSHOTS"201  | "RESTORE_VAULT_SNAPSHOT"202  | "COUNT_DUPLICATES"203  | "DEDUPLICATE_ENTRIES"204  | "RECORD_MANUAL_SUMMON"205  | "DECLINE_CARD_SAVE"206  | "IS_CARD_DECLINED"207  | "SUBMIT_FEEDBACK"208  | "GET_VAULT_STATUS"209  | "GET_ADVANCED_HEALTH_REPORT"210  | "INITIATE_PASSWORD_CHANGE"211  | "DEACTIVATE_LICENSE"212  | "GET_IDENTITIES"213  // Content-script-bound message types — sent BY the background TO content214  // scripts. They never reach messages.ts's switch. Kept in this union so215  // existing call sites type-check; do not add cases for them in the216  // background handler.217  | "PING"            // background → content: liveness probe218  | "SUMMON_AUTOFILL" // background → content: open the in-page palette219  | "VAULT_LOCKED"    // background → content/popup: vault just locked220  | "VAULT_UNLOCKED"  // background → content/popup: vault just unlocked221  | "PRO_GATE_TRIGGERED" // background → popup: a Pro feature was attempted on free222  | "SHOULD_PLAY_CELEBRATION"223  | "MARK_CELEBRATION_PLAYED";224225export type ProFeature =226  | "sync"227  | "passkeys"228  | "advanced_health"229  | "smart_password_change"230  | "ios_app";231232/**233 * License export shape included in CJK phrase and encrypted JSON backups.234 * `celebrationPlayed` is intentionally absent — a new device deserves the moment.235 */236export interface PeachLicenseExport {237  key: string;238  activatedAt?: number;239}240241export interface AdvancedHealthEntryReport {242  id: string;243  title: string;244  domain: string;245  url?: string;246  score: number;247  ageDays: number;248  reasons: string[];249  factors: {250    breached: boolean;251    breachCount: number;252    reused: boolean;253    aged: boolean;254    weak: boolean;255    missingTotpOnSensitive: boolean;256  };257}258259export interface AdvancedHealthReport {260  vaultScore: number;261  generatedAt: number;262  entryCount: number;263  reusedGroups: number;264  agedCount: number;265  remediation: AdvancedHealthEntryReport[];266}267268export const ACCENT_COLORS = [269  { name: "peach",    hex: "#F49C70" },270  { name: "rose",     hex: "#FF8AA6" },271  { name: "plum",     hex: "#D597F2" },272  { name: "lavender", hex: "#A6A8FF" },273  { name: "sky",      hex: "#5BC8EE" },274  { name: "mint",     hex: "#5EE0B0" },275  { name: "sage",     hex: "#B5D870" },276  { name: "butter",   hex: "#FFD45A" },277  { name: "sand",     hex: "#EDBC85" },278  { name: "clay",     hex: "#F08660" },279  { name: "slate",    hex: "#80BEDE" },280  { name: "stone",    hex: "#C8B8A8" },281] as const;282283export type AccentColorName = typeof ACCENT_COLORS[number]["name"];284285export interface SavedIdentity {286  value: string;287  type: "email" | "username";288  lastUsed: number;289  useCount: number;290  exampleDomain?: string;291}292293export interface PasswordPolicy {294  minLength?: number;295  maxLength?: number;296  requiresUppercase?: boolean;297  requiresLowercase?: boolean;298  requiresNumbers?: boolean;299  requiresSymbols?: boolean;300  forbidsSymbols?: boolean;301  allowedSymbols?: string;302  /** Characters the site explicitly disallows. Filtered from every pool before generation. */303  forbiddenChars?: string;304}305306export interface Message {307  type: MessageType;308  payload?: unknown;309}310311export interface MessageResponse {312  ok: boolean;313  data?: unknown;314  error?: string;315  unlocked?: boolean;316}