@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
393 lines • 15.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.clearCache = exports.getCacheKeys = exports.removeCacheItems = exports.removeCacheItem = exports.setCacheItem = exports.getCacheItem = exports.isLocalStorageSupported = exports.MODULE_REVISION = exports.DEFAULT_EXPIRATION = exports.LOCAL_STORGAGE_EXPIRATIONS_KEY = exports.LOCAL_STORAGE_PREFIX = exports.keyPrefix = void 0;
const collections_base_1 = require("../helpers/collections.base");
const debug_1 = require("../helpers/debug");
const flatted_1 = require("../helpers/flatted");
const json_1 = require("../helpers/json");
const objects_1 = require("../helpers/objects");
const typecheckers_1 = require("../helpers/typecheckers");
const consolelogger_1 = require("./consolelogger");
let logger = consolelogger_1.ConsoleLogger.get("localstoragecache");
exports.keyPrefix = "kw$_";
exports.LOCAL_STORAGE_PREFIX = "kwizcom-localstorage-cache";
exports.LOCAL_STORGAGE_EXPIRATIONS_KEY = exports.LOCAL_STORAGE_PREFIX + "-expirations";
exports.DEFAULT_EXPIRATION = 20 * 60 * 1000; // 20 minutes;
/** When caching logic changes (serialization methods, format, schema), the MODULE_REVISION should be incremented
* and all client side apps will need to be rebuilt */
exports.MODULE_REVISION = "1";
/** key (no prefix) is kept in lower case. not case sensitive */
function _getCache() {
let _cache = (0, objects_1.getGlobal)("common_utils_localstoragecache_module_cache");
if (!_cache.purgeCalled) {
//issue 7081 - purge all orphans/expired items
_cache.purgeCalled = true;
//clear expired cache items.
(globalThis || window).setTimeout(() => {
purgeCache();
if ((0, debug_1.isDebug)()) {
let size = _getStoredSize();
logger.debug(`Size of items in local storage: ${size}KB`);
}
}, 5000);
}
return _cache;
}
var _supportsLocalStorage = null;
function _parseExpiration(exp) {
var expirationDate;
if ((0, typecheckers_1.isNumber)(exp) && exp > 0) {
expirationDate = new Date();
expirationDate.setMilliseconds(expirationDate.getMilliseconds() + exp);
}
else if (exp instanceof Date) {
expirationDate = exp;
}
else if (exp) {
var tempexp = exp;
var seconds = typeof (tempexp.seconds) === "number" ? tempexp.seconds : undefined;
var minutes = typeof (tempexp.minutes) === "number" ? tempexp.minutes : undefined;
var hours = typeof (tempexp.hours) === "number" ? tempexp.hours : undefined;
var days = typeof (tempexp.days) === "number" ? tempexp.days : undefined;
var months = typeof (tempexp.months) === "number" ? tempexp.months : undefined;
var years = typeof (tempexp.years) === "number" ? tempexp.years : undefined;
if (seconds || minutes || hours || days || months || years) {
expirationDate = new Date();
if (seconds) {
expirationDate.setMilliseconds(expirationDate.getMilliseconds() + (seconds * 1000));
}
if (minutes) {
expirationDate.setMilliseconds(expirationDate.getMilliseconds() + (minutes * 60 * 1000));
}
if (hours) {
expirationDate.setMilliseconds(expirationDate.getMilliseconds() + (hours * 60 * 60 * 1000));
}
if (days) {
expirationDate.setMilliseconds(expirationDate.getMilliseconds() + (days * 24 * 60 * 60 * 1000));
}
if (months) {
expirationDate.setMonth(expirationDate.getMonth() + months);
}
if (years) {
expirationDate.setFullYear(expirationDate.getFullYear() + years);
}
}
}
if (!expirationDate) {
expirationDate = new Date();
expirationDate.setMilliseconds(expirationDate.getMilliseconds() + exports.DEFAULT_EXPIRATION);
}
return expirationDate;
}
function _getCacheExpirations() {
let _cache = _getCache();
if ((0, typecheckers_1.isNullOrUndefined)(_cache.expirations)) {
_cache.expirations = (0, json_1.jsonParse)(_getItem(exports.LOCAL_STORGAGE_EXPIRATIONS_KEY));
//ISSUE: 1525 - expire the cache if it was built with a different version number so that the cache
//is compatible with the current build
if (!(0, typecheckers_1.isNullOrUndefined)(_cache.expirations) && _cache.expirations.build !== exports.MODULE_REVISION) {
logger.log(`Purging cache because of build number change`);
purgeCache(true);
_cache.expirations = null;
}
if ((0, typecheckers_1.isNullOrUndefined)(_cache.expirations)) {
_cache.expirations = {
build: exports.MODULE_REVISION
};
}
}
return _cache.expirations;
}
function _saveCacheExpirations() {
let _cache = _getCache();
if (!(0, typecheckers_1.isNullOrUndefined)(_cache.expirations) && (0, collections_base_1.sizeOf)(_cache.expirations) > 0) {
_setItem(exports.LOCAL_STORGAGE_EXPIRATIONS_KEY, JSON.stringify(_cache.expirations));
}
else {
_removeItem(exports.LOCAL_STORGAGE_EXPIRATIONS_KEY);
}
}
function _setCacheExpiration(keyWithPrefix, expireDate) {
var expirations = _getCacheExpirations();
expirations[keyWithPrefix] = expireDate.toISOString();
_saveCacheExpirations();
}
function _isKeyExpired(keyWithPrefix) {
var expirations = _getCacheExpirations();
if (expirations && expirations[keyWithPrefix]) {
var now = new Date();
var eDate = new Date(expirations[keyWithPrefix]);
if (now > eDate) {
try {
delete expirations[keyWithPrefix];
}
catch (ex) {
expirations[keyWithPrefix] = undefined; // undefined variables are removed when passed to JSON.stringify
}
_saveCacheExpirations();
//has a date, it is expired.
return true;
}
//has a date, it is not expired yet.
return false;
}
//has no date or not in expirations at all - say it is expired...
return true;
}
function _getItem(key) {
try {
return localStorage.getItem(key);
}
catch {
}
return null;
}
function _setItem(key, value) {
try {
localStorage.setItem(key, value);
return true;
}
catch {
}
return false;
}
function _removeItem(key) {
try {
localStorage.removeItem(key);
return true;
}
catch {
}
return false;
}
/**Get the size (KB) of all entries in local storage. Only returns the size for entries with kwizcom key prefix. */
function _getStoredSize() {
let keys = getCacheKeys();
let total = 0;
let length = 0;
let useBlob = 'Blob' in (globalThis || window);
keys.forEach((key) => {
let v = _getItem(`${exports.keyPrefix}${key}`);
if (!(0, typecheckers_1.isNullOrEmptyString)(v)) {
if (useBlob) {
length = (new Blob([v + key])).size;
}
else {
length = ((v.length + key.length) * 2);
}
}
total += length;
});
return Number((total / 1024).toFixed(2));
}
function isLocalStorageSupported() {
if (_supportsLocalStorage !== null) {
return _supportsLocalStorage;
}
var result;
try {
_setItem(exports.LOCAL_STORAGE_PREFIX, exports.LOCAL_STORAGE_PREFIX);
result = _getItem(exports.LOCAL_STORAGE_PREFIX) === exports.LOCAL_STORAGE_PREFIX;
_removeItem(exports.LOCAL_STORAGE_PREFIX);
_supportsLocalStorage = result;
}
catch (ex) {
_supportsLocalStorage = false;
}
return _supportsLocalStorage;
}
exports.isLocalStorageSupported = isLocalStorageSupported;
//#region exported methods
function getCacheItem(key, options) {
key = key.toLowerCase();
let keyWithPrefix = exports.keyPrefix + key;
let _cache = _getCache();
if (typeof (_cache[key]) !== "undefined"
&& _cache[key] !== null) {
let isExpired = _isKeyExpired(keyWithPrefix);
if (!isExpired) {
return _cache[key];
}
//else remove it from cache
removeCacheItem(key);
}
if (isLocalStorageSupported()) {
var value = _getItem(keyWithPrefix);
if ((0, typecheckers_1.isNullOrUndefined)(value)) {
return null;
}
let isExpired = _isKeyExpired(keyWithPrefix);
if (!isExpired) {
let valueAsT = options && options.useFlatted ? flatted_1.flatted.parse(value) : (0, json_1.jsonParse)(value);
if (valueAsT !== null) {
_cache[key] = valueAsT;
return valueAsT;
}
else {
_cache[key] = value;
return value;
}
}
//else remove it from cache
removeCacheItem(key);
}
return null;
}
exports.getCacheItem = getCacheItem;
function setCacheItem(key, value, expiration, options) {
if (isLocalStorageSupported()) {
key = key.toLowerCase();
removeCacheItem(key);
var val = null;
try {
if (options && options.useFlatted)
val = flatted_1.flatted.stringify(value);
else
val = JSON.stringify(value);
}
catch (ex) {
logger.debug(`Object cannot be stored in local storage: ${ex && ex.message || ex} ${key}`);
return; //this put [object] in cache for me if object can't be stringified!
}
let keyWithPrefix = exports.keyPrefix + key;
var expireDate = _parseExpiration(expiration);
let saved = _setItem(keyWithPrefix, val);
if (saved) {
_setCacheExpiration(keyWithPrefix, expireDate);
}
let _cache = _getCache();
_cache[key] = value;
}
}
exports.setCacheItem = setCacheItem;
function removeCacheItem(keyNoPrefix) {
keyNoPrefix = keyNoPrefix.toLowerCase();
let _cache = _getCache();
delete _cache[keyNoPrefix];
let keyWithPrefix = exports.keyPrefix + keyNoPrefix;
if (isLocalStorageSupported()) {
_removeItem(keyNoPrefix); //in case we have an old one
_removeItem(keyWithPrefix);
}
}
exports.removeCacheItem = removeCacheItem;
function removeCacheItems(keys) {
keys.forEach((key) => {
removeCacheItem(key);
});
}
exports.removeCacheItems = removeCacheItems;
function getCacheKeys() {
let keys = [];
if (isLocalStorageSupported()) {
keys = Object.keys(localStorage).filter((key) => {
return key.startsWith(exports.keyPrefix);
}).map((key) => {
return key.substring(exports.keyPrefix.length);
});
}
return keys;
}
exports.getCacheKeys = getCacheKeys;
/** remove expired cache keys created by this utility.
* to remove all keys (non-expired too) send removeAll=true
*/
function purgeCache(removeAll) {
if (!isLocalStorageSupported())
return;
var cacheExpirationsKeys = [
exports.LOCAL_STORGAGE_EXPIRATIONS_KEY,
"kwizcom-aplfe-caching-expirations", // old clean up
"localStorageExpirations" // old clean up
];
let now = new Date();
let nonExpiredKeys = [];
//get all expiration keys (key/expiration date/time)
for (let j = 0; j < cacheExpirationsKeys.length; j++) {
try {
let expirations = null;
let cacheExpirationsKey = cacheExpirationsKeys[j];
let removeAllForKey = removeAll || cacheExpirationsKey !== exports.LOCAL_STORGAGE_EXPIRATIONS_KEY;
if (cacheExpirationsKey === "localStorageExpirations") {
//old format - load expirations from this one as well
expirations = _getItem(cacheExpirationsKey); // "key1^11/18/2011 5pm|key2^3/10/2012 3pm"
if (expirations) {
let arr = expirations.split("|"); // ["key1^11/18/2011 5pm","key2^3/10/2012 3pm"]
for (let i = 0; i < arr.length; i++) {
try {
let key_expiration_format = arr[i]; // "key1^11/18/2011 5pm"
let key = key_expiration_format.split("^")[0];
//old keys - remove all, all the time
_removeItem(key); //remove key from cache
}
catch (e) { }
}
}
}
else {
//new format
expirations = cacheExpirationsKey === exports.LOCAL_STORGAGE_EXPIRATIONS_KEY ? _getCacheExpirations() : (0, json_1.jsonParse)(_getItem(cacheExpirationsKey));
if (expirations) {
let expirationKeys = Object.keys(expirations);
logger.group(() => {
expirationKeys.forEach(keyWithPrefix => {
try {
let shouldRemoveKey = removeAllForKey || !keyWithPrefix.startsWith(exports.keyPrefix);
if (!shouldRemoveKey) {
//check specific key expiration
let expirationDate = new Date(expirations[keyWithPrefix]);
if (!(0, typecheckers_1.isDate)(expirationDate) || expirationDate < now) {
shouldRemoveKey = true;
delete expirations[keyWithPrefix];
logger.info(`purging key ${keyWithPrefix}`);
}
else {
nonExpiredKeys.push(keyWithPrefix);
}
}
if (shouldRemoveKey)
_removeItem(keyWithPrefix);
}
catch (e) {
logger.warn(`failed to remove key ${keyWithPrefix}`);
}
});
}, "Checking expired items", true);
}
}
if (cacheExpirationsKey === exports.LOCAL_STORGAGE_EXPIRATIONS_KEY)
_saveCacheExpirations();
else //older keys - just remove them.
_removeItem(cacheExpirationsKey);
}
catch (e) {
logger.warn(`something went terribly wrong ${e}`);
}
}
logger.group(() => {
logger.table(nonExpiredKeys);
//cleanup orphans
//loop on all keys
//if stats with: jsr_, kwfs| or keyPrefix - and not in nonExpiredKeys, it is an orphan. Remove it.
let localStorageKeys = Object.keys(localStorage);
for (let keyIdx = 0; keyIdx < localStorageKeys.length; keyIdx++) {
let key = localStorageKeys[keyIdx];
if (key.startsWith("jsr_") || key.startsWith("kwfs|")) {
logger.log(`removing old key ${key}`);
_removeItem(key); //old key
}
else if (key.startsWith(exports.keyPrefix) && !nonExpiredKeys.includes(key)) //orphan!
{
logger.log(`removing orphan key ${key}`);
_removeItem(key);
}
}
}, "Expired keys", true);
}
/** cleanup - remove all local storage keys created by this utility */
function clearCache() {
return purgeCache(true);
}
exports.clearCache = clearCache;
//#endregion
//# sourceMappingURL=localstoragecache.js.map