- Added coverage reporting for Vitest with configuration in vitest.config.mjs - Updated package.json to include new scripts for JS coverage, linting, and formatting - Modified run_quality_gate.py to allow skipping ESLint and Prettier checks - Introduced .prettierrc for consistent code formatting - Created .prettierignore to exclude specific files and directories from formatting
131 lines
3.4 KiB
JavaScript
131 lines
3.4 KiB
JavaScript
/**
|
|
* Centralized key/value storage wrapper for browser settings.
|
|
* Uses localStorage when available and notifies subscribers on changes.
|
|
*/
|
|
|
|
const _subscribers = new Map();
|
|
let _storageListenerBound = false;
|
|
let _storageListener = null;
|
|
|
|
function _getStorage() {
|
|
try {
|
|
if (typeof window === "undefined") return null;
|
|
const ls = window.localStorage;
|
|
if (!ls) return null;
|
|
return ls;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function _emit(key, nextValue, previousValue) {
|
|
const set = _subscribers.get(String(key || ""));
|
|
if (!set || !set.size) return;
|
|
for (const cb of Array.from(set)) {
|
|
try {
|
|
cb(nextValue, previousValue, key);
|
|
} catch (e) {
|
|
console.debug?.(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
function _bindStorageListener() {
|
|
if (_storageListenerBound) return;
|
|
try {
|
|
_storageListener = (event) => {
|
|
const key = String(event?.key || "");
|
|
if (!key) return;
|
|
_emit(key, event?.newValue ?? null, event?.oldValue ?? null);
|
|
};
|
|
window.addEventListener("storage", _storageListener);
|
|
_storageListenerBound = true;
|
|
} catch (e) {
|
|
console.debug?.(e);
|
|
}
|
|
}
|
|
|
|
export const SettingsStore = {
|
|
get(key) {
|
|
const storage = _getStorage();
|
|
if (!storage) return null;
|
|
try {
|
|
return storage.getItem(String(key || ""));
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
set(key, value) {
|
|
const k = String(key || "");
|
|
if (!k) return false;
|
|
const storage = _getStorage();
|
|
if (!storage) return false;
|
|
const previousValue = SettingsStore.get(k);
|
|
try {
|
|
if (value == null) {
|
|
storage.removeItem(k);
|
|
_emit(k, null, previousValue);
|
|
return true;
|
|
}
|
|
const nextValue = String(value);
|
|
storage.setItem(k, nextValue);
|
|
_emit(k, nextValue, previousValue);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
subscribe(key, cb) {
|
|
const k = String(key || "");
|
|
if (!k || typeof cb !== "function") return () => {};
|
|
_bindStorageListener();
|
|
let set = _subscribers.get(k);
|
|
if (!set) {
|
|
set = new Set();
|
|
_subscribers.set(k, set);
|
|
}
|
|
set.add(cb);
|
|
return () => {
|
|
try {
|
|
const cur = _subscribers.get(k);
|
|
cur?.delete(cb);
|
|
if (cur && !cur.size) _subscribers.delete(k);
|
|
} catch (e) {
|
|
console.debug?.(e);
|
|
}
|
|
};
|
|
},
|
|
|
|
getAll() {
|
|
const out = {};
|
|
const storage = _getStorage();
|
|
if (!storage) return out;
|
|
try {
|
|
const len = Number(storage.length || 0) || 0;
|
|
for (let i = 0; i < len; i += 1) {
|
|
const key = storage.key(i);
|
|
if (!key) continue;
|
|
out[key] = storage.getItem(key);
|
|
}
|
|
} catch (e) {
|
|
console.debug?.(e);
|
|
}
|
|
return out;
|
|
},
|
|
|
|
dispose() {
|
|
try {
|
|
if (_storageListenerBound && _storageListener && typeof window !== "undefined") {
|
|
window.removeEventListener("storage", _storageListener);
|
|
}
|
|
} catch (e) {
|
|
console.debug?.(e);
|
|
}
|
|
_storageListenerBound = false;
|
|
_storageListener = null;
|
|
_subscribers.clear();
|
|
},
|
|
};
|