experiments in a post-browser web
at main 124 lines 3.2 kB view raw
1const openStore = (prefix, defaults, clear = false) => { 2 3 //console.log('openStore', prefix, (defaults ? Object.keys(defaults) : '')); 4 5 // multiple contexts 6 const keyify = k => `${prefix}+${k}`; 7 8 // Simple localStorage abstraction/wrapper 9 const store = { 10 set: (k, v) => { 11 const key = keyify(k); 12 const value = JSON.stringify(v); 13 //console.log('store.set', key, value) 14 localStorage.setItem(key, value); 15 }, 16 get: (k) => { 17 const key = keyify(k); 18 //console.log('store.get', key) 19 const r = localStorage.getItem(key); 20 return r ? JSON.parse(r) : null; 21 }, 22 clear: () => localStorage.clear() 23 }; 24 25 if (window.app.debug 26 && window.app.debugLevel == window.app.debugLevels.FIRST_RUN) { 27 console.log('openStore(): clearing storage') 28 store.clear(); 29 } 30 31 if (clear) { 32 console.log('openStore(): CLEARING'); 33 store.clear(); 34 } 35 36 const initStore = (store, data) => { 37 Object.keys(data).forEach(k => { 38 const v = store.get(k); 39 if (!v) { 40 //console.log('openStore(): init is setting', k, data[k]); 41 store.set(k, data[k]); 42 } 43 }); 44 }; 45 46 if (defaults != null) { 47 //console.log('UTILS/openStore()', 'initing'); 48 initStore(store, defaults); 49 } 50 51 return store; 52}; 53 54// The flattenObj helper is now private - it's only needed for window.open 55 56/** 57 * Create an async store backed by datastore instead of localStorage. 58 * Uses the extension_settings table with extensionId as namespace. 59 * 60 * @param {string} namespace - The extensionId (e.g., 'core', 'cmd', 'scripts') 61 * @param {object} defaults - Default values for each key 62 * @returns {Promise<object>} Store with async get/set methods 63 */ 64const createDatastoreStore = async (namespace, defaults = {}) => { 65 const api = window.app; 66 const table = 'extension_settings'; 67 68 // Load all settings for this namespace once 69 let cache = {}; 70 try { 71 const result = await api.datastore.getTable(table); 72 if (result.success && result.data) { 73 Object.values(result.data).forEach(row => { 74 if (row.extensionId === namespace && row.value) { 75 try { 76 cache[row.key] = JSON.parse(row.value); 77 } catch (e) { 78 console.warn(`[datastoreStore] Failed to parse ${namespace}:${row.key}`, e); 79 } 80 } 81 }); 82 } 83 } catch (e) { 84 console.warn(`[datastoreStore] Failed to load ${namespace}`, e); 85 } 86 87 // Apply defaults for missing keys 88 Object.keys(defaults).forEach(key => { 89 if (cache[key] === undefined) { 90 cache[key] = defaults[key]; 91 } 92 }); 93 94 return { 95 get(key) { 96 return cache[key] !== undefined ? cache[key] : defaults[key]; 97 }, 98 99 async set(key, value) { 100 cache[key] = value; 101 const rowId = `${namespace}:${key}`; 102 try { 103 await api.datastore.setRow(table, rowId, { 104 extensionId: namespace, 105 key, 106 value: JSON.stringify(value), 107 updatedAt: Date.now() 108 }); 109 } catch (e) { 110 console.error(`[datastoreStore] Failed to save ${namespace}:${key}`, e); 111 } 112 }, 113 114 // Get all cached values 115 getAll() { 116 return { ...cache }; 117 } 118 }; 119}; 120 121export { 122 openStore, 123 createDatastoreStore 124};