1234567891011import { capDeviceName } from "../shared/sync-limits";1213export interface SyncAlert {14 type: "DEVICE_KEY_ROTATED" | "DEVICE_REVOKED";15 161718 entryId: string;19 entryTitle: string;20 2122 deviceName: string;23 timestamp: number;24}2526const SYNC_ALERTS_KEY = "peach_pending_sync_alerts_v1";2728const MAX_PENDING_ALERTS = 200;2930export async function recordSyncAlerts(alerts: SyncAlert[]): Promise<void> {31 32 33 const securityAlerts = alerts.filter(isSyncAlert).map((a) => ({34 ...a,35 deviceName: capDeviceName(a.deviceName),36 }));37 if (securityAlerts.length === 0) return;38 const existing = await readAlerts();39 const merged = [...existing, ...securityAlerts].slice(-MAX_PENDING_ALERTS);40 try {41 await chrome.storage.local.set({ [SYNC_ALERTS_KEY]: merged });42 } catch {43 44 }45}4647export async function getPendingSyncAlerts(): Promise<SyncAlert[]> {48 return readAlerts();49}50export async function clearPendingSyncAlerts(): Promise<void> {51 try {52 await chrome.storage.local.remove(SYNC_ALERTS_KEY);53 } catch {54 55 }56}5758export async function dismissPendingSyncAlerts(type: SyncAlert["type"]): Promise<void> {59 try {60 const remaining = (await readAlerts()).filter((alert) => alert.type !== type);61 if (remaining.length === 0) {62 await chrome.storage.local.remove(SYNC_ALERTS_KEY);63 } else {64 await chrome.storage.local.set({ [SYNC_ALERTS_KEY]: remaining });65 }66 } catch {67 68 }69}7071async function readAlerts(): Promise<SyncAlert[]> {72 try {73 const result = await chrome.storage.local.get(SYNC_ALERTS_KEY);74 const value = (result as Record<string, unknown>)[SYNC_ALERTS_KEY];75 if (!Array.isArray(value)) return [];76 return value.filter(isSyncAlert);77 } catch {78 return [];79 }80}8182function isSyncAlert(x: unknown): x is SyncAlert {83 if (typeof x !== "object" || x === null) return false;84 const r = x as Record<string, unknown>;85 return (86 (r.type === "DEVICE_KEY_ROTATED" ||87 r.type === "DEVICE_REVOKED") &&88 typeof r.entryId === "string" &&89 typeof r.entryTitle === "string" &&90 typeof r.deviceName === "string" &&91 typeof r.timestamp === "number"92 );93}