123456789101112import { capDeviceName } from "../shared/sync-limits";1314export interface SyncAlert {15 type: "DEVICE_KEY_ROTATED" | "DEVICE_REVOKED";16 171819 entryId: string;20 entryTitle: string;21 2223 deviceName: string;24 timestamp: number;25}2627const SYNC_ALERTS_KEY = "peach_pending_sync_alerts_v1";2829const MAX_PENDING_ALERTS = 200;3031export async function recordSyncAlerts(alerts: SyncAlert[]): Promise<void> {32 33 34 const securityAlerts = alerts.filter(isSyncAlert).map((a) => ({35 ...a,36 deviceName: capDeviceName(a.deviceName),37 }));38 if (securityAlerts.length === 0) return;39 const existing = await readAlerts();40 const merged = [...existing, ...securityAlerts].slice(-MAX_PENDING_ALERTS);41 try {42 await chrome.storage.local.set({ [SYNC_ALERTS_KEY]: merged });43 } catch {44 45 }46}4748export async function getPendingSyncAlerts(): Promise<SyncAlert[]> {49 return readAlerts();50}5152export async function clearPendingSyncAlerts(): Promise<void> {53 try {54 await chrome.storage.local.remove(SYNC_ALERTS_KEY);55 } catch {56 57 }58}5960async function readAlerts(): Promise<SyncAlert[]> {61 try {62 const result = await chrome.storage.local.get(SYNC_ALERTS_KEY);63 const value = (result as Record<string, unknown>)[SYNC_ALERTS_KEY];64 if (!Array.isArray(value)) return [];65 return value.filter(isSyncAlert);66 } catch {67 return [];68 }69}7071function isSyncAlert(x: unknown): x is SyncAlert {72 if (typeof x !== "object" || x === null) return false;73 const r = x as Record<string, unknown>;74 return (75 (r.type === "DEVICE_KEY_ROTATED" ||76 r.type === "DEVICE_REVOKED") &&77 typeof r.entryId === "string" &&78 typeof r.entryTitle === "string" &&79 typeof r.deviceName === "string" &&80 typeof r.timestamp === "number"81 );82}