38 lines
1 KiB
JavaScript
38 lines
1 KiB
JavaScript
|
/**
|
||
|
* @template {Record<string, any>} T
|
||
|
* @param {string} keyPrefix
|
||
|
* @param {T} defaultState
|
||
|
* @returns {T}
|
||
|
*/
|
||
|
function State(keyPrefix, defaultState) {
|
||
|
/** @param {string} key */
|
||
|
function getFromRegistry(key) {
|
||
|
try {
|
||
|
// TODO validate type
|
||
|
const rawValue = localStorage.getItem(keyPrefix+key);
|
||
|
if (rawValue == null) return null;
|
||
|
return JSON.parse(rawValue);
|
||
|
} catch {
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
return new Proxy(/**@type T*/(Object.fromEntries(
|
||
|
Object.entries(defaultState)
|
||
|
.map(([k, v]) => [k, getFromRegistry(k) ?? v]),
|
||
|
)), {
|
||
|
get(cache, key) {
|
||
|
if (typeof key === 'symbol') return undefined;
|
||
|
// cache hit
|
||
|
if (key in cache) return cache[key];
|
||
|
// cache miss => get from localStorage
|
||
|
return /**@type{any}*/(cache)[key] = getFromRegistry(key);
|
||
|
},
|
||
|
set(cache, key, value) {
|
||
|
if (typeof key === 'symbol') return false;
|
||
|
/**@type{any}*/(cache)[key] = value;
|
||
|
localStorage.setItem(keyPrefix+key, JSON.stringify(value));
|
||
|
return true;
|
||
|
},
|
||
|
});
|
||
|
};
|