kk-date
Version:
kk-date is a fastest JavaScript library that parses, validations, manipulates, and displays dates and times. If you use Moment.js or Day.js already you can easily use kk-date.
424 lines (395 loc) • 9.36 kB
JavaScript
/* eslint-disable no-inner-declarations */
/**
* nope-redis v1.3.5 editted
* https://www.npmjs.com/package/nope-redis
*/
let defaultTtl = 1300;
let criticalError = 0;
let KILL_SERVICE = false;
const intervalSecond = 5;
let runnerInterval = null;
const MAX_CACHE_SIZE = 10000; // Maximum number of items in cache
const memory = {
config: {
status: false,
killerIsFinished: true,
lastKiller: 0,
nextKiller: 0,
totalHits: 0,
nextMemoryStatsTime: 0,
memoryStats: {},
totalKeys: 0,
},
store: {},
// LRU tracking
lru: new Map(), // key -> timestamp
};
/**
* no-redis config
*
* @param {object} options
* @returns {boolean}
*/
module.exports.config = (options = { defaultTtl }) => {
try {
if (memory.config.status === false) {
return false;
}
if (typeof options === 'object') {
if (typeof options.defaultTtl === 'number' && options.defaultTtl > 0) {
const now_ttl = Number.parseInt(options.defaultTtl, 10);
if (typeof now_ttl === 'number') {
defaultTtl = now_ttl;
}
}
return true;
}
} catch (error) {
console.error('nope-redis -> config error', error.message);
}
return false;
};
/**
* LRU eviction - remove least recently used items
*/
function evictLRU() {
if (memory.config.totalKeys <= MAX_CACHE_SIZE) {
return;
}
// Sort by access time (least recent first)
const sortedKeys = Array.from(memory.lru.entries())
.sort((a, b) => a[1] - b[1])
.map((entry) => entry[0]);
// Remove oldest 20% of items
const itemsToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);
const keysToRemove = sortedKeys.slice(0, itemsToRemove);
// biome-ignore lint/complexity/noForEach: <explanation>
keysToRemove.forEach((key) => {
delete memory.store[key];
memory.lru.delete(key);
memory.config.totalKeys--;
});
}
/**
* set item to no-redis (sync version for performance)
*
* @param {string} key
* @param {*} value
* @param {number} ttl
* @returns {Boolean}
*/
module.exports.setItemSync = (key, value, ttl = defaultTtl) => {
try {
const keyStr = `${key}`;
if (!memory.config.status || typeof ttl !== 'number') {
return false;
}
// Check if we need to evict items
if (memory.config.totalKeys > MAX_CACHE_SIZE) {
evictLRU();
}
memory.store[keyStr] = {
value: value,
hit: 0,
expires_at: Math.floor(new Date() / 1000) + Number.parseInt(ttl, 10),
};
// Update LRU tracking
memory.lru.set(keyStr, Date.now());
memory.config.totalKeys++;
return true;
} catch (error) {
console.error('nope-redis -> Cant Set Error! ', error.message);
return false;
}
};
/**
* set item to no-redis (async version)
*
* @param {string} key
* @param {*} value
* @param {number} ttl
* @returns {Boolean}
*/
module.exports.setItemAsync = async (key, value, ttl = defaultTtl) => {
return module.exports.setItemSync(key, value, ttl);
};
/**
* get item stats
*
* @param {string} key
* @returns {object}
*/
module.exports.itemStats = (key) => {
try {
if (memory.store[`${key}`]) {
return {
expires_at: memory.store[`${key}`].expires_at,
remaining_seconds: memory.store[`${key}`].expires_at - Math.floor(new Date() / 1000),
hit: memory.store[`${key}`].hit,
};
}
return null;
} catch (error) {
console.error('nope-redis -> Cant get item stats Error! ', error.message);
return false;
}
};
/**
* get item from no-redis
*
* @param {string} key
* @returns {*}
*/
module.exports.getItem = (key) => {
if (!key) return null;
const keyStr = `${key}`;
try {
if (!memory.config.status) {
return false;
}
if (memory.store[keyStr]) {
memory.store[keyStr].hit++;
memory.config.totalHits++;
// Update LRU tracking on access
memory.lru.set(keyStr, Date.now());
return memory.store[keyStr].value;
}
return null;
} catch (error) {
console.error('nope-redis -> Crital error! ', error.message);
return false;
}
};
/**
* delete item from no-redis
*
* @param {string} key
* @returns {Boolean}
*/
module.exports.deleteItem = (key) => {
try {
const keyStr = `${key}`;
if (memory.config.status === false) {
return false;
}
if (memory.store[keyStr]) {
delete memory.store[keyStr];
memory.lru.delete(keyStr);
memory.config.totalKeys--;
}
return true;
} catch (error) {
console.error('nope-redis -> Cant delete item', error.message);
return false;
}
};
/**
* flush all data
*
* @returns {Boolean}
*/
module.exports.flushAll = () => {
try {
if (memory.config.status === false) {
return false;
}
// just store clean
defaultMemory(false);
return true;
} catch (error) {
console.error('nope-redis -> Cant flush!', error.message);
return false;
}
};
/**
* get stats from noRedis
*
* @param {object} config
* @returns {object}
*/
module.exports.stats = (config = { showKeys: true, showTotal: true, showSize: false }) => {
try {
const result = {
status: memory.config.status,
killerIsFinished: memory.config.killerIsFinished,
lastKiller: memory.config.lastKiller,
nextKiller: memory.config.nextKiller,
criticalError,
defaultTtl,
totalHits: memory.config.totalHits,
cacheSize: Object.keys(memory.store).length,
maxCacheSize: MAX_CACHE_SIZE,
};
if (config.showTotal) {
result.total = Object.keys(memory.store).length;
}
if (config.showSize) {
result.size = roughSizeOfObject(memory.store);
}
if (config.showKeys) {
result.keys = Object.keys(memory.store);
}
return result;
} catch (error) {
console.error('nope-redis -> stats error!', error.message);
return false;
}
};
/**
* default memory set
*
* @param {Boolean} withConfig
* @returns {Boolean}
*/
function defaultMemory(withConfig = false) {
try {
const defaultMemory = {
config: {
killerIsFinished: true,
lastKiller: 0,
nextKiller: 0,
totalHits: 0,
nextMemoryStatsTime: 0,
status: false,
memoryStats: {},
totalKeys: 0,
},
};
memory.store = {};
memory.lru.clear();
if (withConfig) {
memory.config = JSON.parse(JSON.stringify(defaultMemory.config));
}
} catch (error) {
console.error('nope-redis -> Cant default memory!', error.message);
return false;
}
}
/**
* get object size
*
* @param {object} object
* @returns {string}
*/
function roughSizeOfObject(object) {
try {
function formatSizeUnits(unit_bytes) {
if (bytes >= 1073741824) {
return `${(unit_bytes / 1073741824).toFixed(2)} GB`;
}
if (unit_bytes >= 1048576) {
return `${(unit_bytes / 1048576).toFixed(2)} MB`;
}
if (unit_bytes >= 1024) {
return `${(unit_bytes / 1024).toFixed(2)} KB`;
}
if (unit_bytes > 1) {
return `${unit_bytes} bytes`;
}
if (unit_bytes === 1) {
return `${unit_bytes} byte`;
}
return '0 bytes';
}
const objectList = [];
const stack = [object];
let bytes = 0;
while (stack.length) {
const value = stack.pop();
if (typeof value === 'boolean') {
bytes += 4;
} else if (typeof value === 'string') {
bytes += value.length * 2;
} else if (typeof value === 'number') {
bytes += 8;
} else if (typeof value === 'object' && objectList.indexOf(value) === -1) {
objectList.push(value);
for (const i in value) {
stack.push(value[i]);
}
}
}
return formatSizeUnits(bytes);
} catch (error) {
console.error('nope-redis -> roughSizeOfObject error!', error.message);
return 'Error !';
}
}
/**
* deleter for expired key
*/
function killer() {
memory.config.killerIsFinished = false;
for (const property in memory.store) {
if (memory.store[`${property}`].expires_at < Math.floor(new Date() / 1000)) {
delete memory.store[`${property}`];
memory.config.totalKeys--;
}
}
memory.config.killerIsFinished = true;
memory.config.lastKiller = Math.floor(new Date() / 1000);
}
module.exports.SERVICE_KILL = async () => {
KILL_SERVICE = true;
return true;
};
module.exports.SERVICE_KILL_SYNC = () => {
KILL_SERVICE = true;
clearInterval(runnerInterval);
defaultMemory(true);
KILL_SERVICE = false;
return true;
};
module.exports.SERVICE_START = () => {
if (KILL_SERVICE === false && memory.config.status === false && memory.config.lastKiller === 0) {
return runner();
}
return false;
};
/**
* init runner
*/
function runner() {
try {
if (memory.config.status === false) {
if (criticalError <= 3) {
memory.config.status = true;
} else {
console.error('nope-redis -> critic error, nope-redis not started');
return false;
}
}
runnerInterval = setInterval(() => {
try {
if (KILL_SERVICE) {
clearInterval(runnerInterval);
defaultMemory(true);
KILL_SERVICE = false;
return true;
}
if (memory.config.killerIsFinished) {
killer();
}
memory.config.nextKiller = Math.floor(new Date() / 1000) + intervalSecond;
} catch (error) {
console.error('nope-redis -> Critical Error flushed all data! > ', error.message);
clearInterval(runnerInterval);
defaultMemory(true);
criticalError++;
runner();
}
}, intervalSecond * 1000);
} catch (error) {
console.error('nope-redis -> Critical Error flushed all data! > ', error.message);
if (typeof runnerInterval !== 'undefined') {
clearInterval(runnerInterval);
}
defaultMemory(true);
criticalError++;
if (memory.config.status === false) {
runner();
}
return false;
}
}
module.exports.runner = runner;