[READ-ONLY] a fast, modern browser for the npm registry
at main 109 lines 2.5 kB view raw
1import { createDefu } from 'defu' 2 3/** 4 * Abstraction for preferences storage 5 * Currently uses localStorage, designed for future user prefs API 6 */ 7 8const STORAGE_KEY = 'npmx-list-prefs' 9 10interface StorageProvider<T> { 11 get: () => T | null 12 set: (value: T) => void 13 remove: () => void 14} 15 16const defu = createDefu((object, key, value) => { 17 if (Array.isArray(object[key]) && Array.isArray(value)) { 18 object[key] = value 19 return true 20 } 21}) 22 23/** 24 * Creates a localStorage-based storage provider 25 */ 26function createLocalStorageProvider<T>(key: string): StorageProvider<T> { 27 return { 28 get: () => { 29 if (import.meta.server) return null 30 try { 31 const stored = localStorage.getItem(key) 32 if (stored) { 33 return JSON.parse(stored) as T 34 } 35 } catch { 36 // Corrupted data, remove it 37 localStorage.removeItem(key) 38 } 39 return null 40 }, 41 set: (value: T) => { 42 if (import.meta.server) return 43 try { 44 localStorage.setItem(key, JSON.stringify(value)) 45 } catch { 46 // Storage full or other error, fail silently 47 } 48 }, 49 remove: () => { 50 if (import.meta.server) return 51 localStorage.removeItem(key) 52 }, 53 } 54} 55 56// Future: API-based provider would look like this: 57// function createApiStorageProvider<T>(endpoint: string): StorageProvider<T> { 58// return { 59// get: async () => { /* fetch from API */ }, 60// set: async (value) => { /* POST to API */ }, 61// remove: async () => { /* DELETE from API */ }, 62// } 63// } 64 65/** 66 * Composable for managing preferences storage 67 * Abstracts the storage mechanism to allow future migration to API-based storage 68 * 69 */ 70export function usePreferencesProvider<T extends object>(defaultValue: T) { 71 const provider = createLocalStorageProvider<T>(STORAGE_KEY) 72 const data = ref<T>(defaultValue) 73 const isHydrated = shallowRef(false) 74 75 // Load from storage on client 76 onMounted(() => { 77 const stored = provider.get() 78 if (stored) { 79 // Merge stored values with defaults to handle schema evolution 80 data.value = defu(stored, defaultValue) 81 } 82 isHydrated.value = true 83 }) 84 85 // Persist changes 86 function save() { 87 provider.set(data.value) 88 } 89 90 // Reset to defaults 91 function reset() { 92 data.value = { ...defaultValue } 93 provider.remove() 94 } 95 96 // Update specific keys 97 function update<K extends keyof T>(key: K, value: T[K]) { 98 data.value[key] = value 99 save() 100 } 101 102 return { 103 data, 104 isHydrated, 105 save, 106 reset, 107 update, 108 } 109}