Spaces:
Sleeping
Sleeping
File size: 4,367 Bytes
b64de39 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | /**
* LocalStorage wrapper with safe JSON serialization.
*
* All read/write operations are wrapped in try/catch to handle
* environments where localStorage is unavailable (SSR, privacy
* mode, storage quota exceeded, etc.).
*/
// ---------------------------------------------------------------------------
// Availability Check
// ---------------------------------------------------------------------------
/**
* Determine whether localStorage is available and writable.
*
* @returns {boolean}
*/
function isStorageAvailable() {
try {
const testKey = "__storage_test__";
window.localStorage.setItem(testKey, "1");
window.localStorage.removeItem(testKey);
return true;
} catch {
return false;
}
}
const storageAvailable = isStorageAvailable();
// ---------------------------------------------------------------------------
// Core Operations
// ---------------------------------------------------------------------------
/**
* Retrieve a value from localStorage.
*
* @param {string} key - Storage key.
* @param {*} defaultValue - Fallback when the key is absent or unreadable.
* @returns {*} Parsed value or defaultValue.
*/
export function getItem(key, defaultValue = null) {
if (!storageAvailable) {
return defaultValue;
}
try {
const raw = window.localStorage.getItem(key);
if (raw === null) {
return defaultValue;
}
return JSON.parse(raw);
} catch {
return defaultValue;
}
}
/**
* Persist a value to localStorage.
*
* @param {string} key - Storage key.
* @param {*} value - Value to store (will be JSON-serialized).
* @returns {boolean} Whether the write succeeded.
*/
export function setItem(key, value) {
if (!storageAvailable) {
return false;
}
try {
window.localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (err) {
console.error(`[storage] Failed to write key "${key}":`, err);
return false;
}
}
/**
* Remove a key from localStorage.
*
* @param {string} key - Storage key.
* @returns {boolean} Whether the removal succeeded.
*/
export function removeItem(key) {
if (!storageAvailable) {
return false;
}
try {
window.localStorage.removeItem(key);
return true;
} catch {
return false;
}
}
/**
* Clear all FarmHelp-related entries from localStorage.
* Only removes keys that start with "farmhelp_".
*
* @returns {boolean} Whether the operation succeeded.
*/
export function clearAll() {
if (!storageAvailable) {
return false;
}
try {
const keysToRemove = [];
for (let i = 0; i < window.localStorage.length; i++) {
const key = window.localStorage.key(i);
if (key && key.startsWith("farmhelp_")) {
keysToRemove.push(key);
}
}
keysToRemove.forEach((key) => window.localStorage.removeItem(key));
return true;
} catch {
return false;
}
}
// ---------------------------------------------------------------------------
// TTL-Based Cache Operations
// ---------------------------------------------------------------------------
/**
* Store a value with an expiration timestamp.
*
* @param {string} key - Storage key.
* @param {*} value - Value to cache.
* @param {number} ttlMs - Time-to-live in milliseconds.
* @returns {boolean} Whether the write succeeded.
*/
export function setWithTTL(key, value, ttlMs) {
const record = {
value,
expiry: Date.now() + ttlMs,
};
return setItem(key, record);
}
/**
* Retrieve a cached value, returning defaultValue if expired or absent.
*
* @param {string} key - Storage key.
* @param {*} defaultValue - Fallback when expired or absent.
* @returns {*} The cached value or defaultValue.
*/
export function getWithTTL(key, defaultValue = null) {
const record = getItem(key, null);
if (!record || typeof record !== "object" || !record.expiry) {
return defaultValue;
}
if (Date.now() > record.expiry) {
removeItem(key);
return defaultValue;
}
return record.value;
}
// ---------------------------------------------------------------------------
// Convenience Export
// ---------------------------------------------------------------------------
const storage = {
get: getItem,
set: setItem,
remove: removeItem,
clearAll,
setWithTTL,
getWithTTL,
};
export default storage;
|