You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@cacheable/utils

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cacheable/utils - npm Package Compare versions

Comparing version
1.1.1
to
2.0.0
+1
-468
dist/index.cjs

@@ -1,468 +0,1 @@

"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
HashAlgorithm: () => HashAlgorithm,
Stats: () => Stats,
calculateTtlFromExpiration: () => calculateTtlFromExpiration,
coalesceAsync: () => coalesceAsync,
getCascadingTtl: () => getCascadingTtl,
getTtlFromExpires: () => getTtlFromExpires,
hash: () => hash,
hashToNumber: () => hashToNumber,
shorthandToMilliseconds: () => shorthandToMilliseconds,
shorthandToTime: () => shorthandToTime,
sleep: () => sleep
});
module.exports = __toCommonJS(index_exports);
// src/shorthand-time.ts
var shorthandToMilliseconds = (shorthand) => {
let milliseconds;
if (shorthand === void 0) {
return void 0;
}
if (typeof shorthand === "number") {
milliseconds = shorthand;
} else if (typeof shorthand === "string") {
shorthand = shorthand.trim();
if (Number.isNaN(Number(shorthand))) {
const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand);
if (!match) {
throw new Error(
`Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`
);
}
const [, value, unit] = match;
const numericValue = Number.parseFloat(value);
const unitLower = unit.toLowerCase();
switch (unitLower) {
case "ms": {
milliseconds = numericValue;
break;
}
case "s": {
milliseconds = numericValue * 1e3;
break;
}
case "m": {
milliseconds = numericValue * 1e3 * 60;
break;
}
case "h": {
milliseconds = numericValue * 1e3 * 60 * 60;
break;
}
case "hr": {
milliseconds = numericValue * 1e3 * 60 * 60;
break;
}
case "d": {
milliseconds = numericValue * 1e3 * 60 * 60 * 24;
break;
}
/* c8 ignore next 3 */
default: {
milliseconds = Number(shorthand);
}
}
} else {
milliseconds = Number(shorthand);
}
} else {
throw new TypeError("Time must be a string or a number.");
}
return milliseconds;
};
var shorthandToTime = (shorthand, fromDate) => {
fromDate ??= /* @__PURE__ */ new Date();
const milliseconds = shorthandToMilliseconds(shorthand);
if (milliseconds === void 0) {
return fromDate.getTime();
}
return fromDate.getTime() + milliseconds;
};
// src/coalesce-async.ts
var callbacks = /* @__PURE__ */ new Map();
function hasKey(key) {
return callbacks.has(key);
}
function addKey(key) {
callbacks.set(key, []);
}
function removeKey(key) {
callbacks.delete(key);
}
function addCallbackToKey(key, callback) {
const stash = getCallbacksByKey(key);
stash.push(callback);
callbacks.set(key, stash);
}
function getCallbacksByKey(key) {
return callbacks.get(key) ?? [];
}
async function enqueue(key) {
return new Promise((resolve, reject) => {
const callback = { resolve, reject };
addCallbackToKey(key, callback);
});
}
function dequeue(key) {
const stash = getCallbacksByKey(key);
removeKey(key);
return stash;
}
function coalesce(options) {
const { key, error, result } = options;
for (const callback of dequeue(key)) {
if (error) {
callback.reject(error);
} else {
callback.resolve(result);
}
}
}
async function coalesceAsync(key, fnc) {
if (!hasKey(key)) {
addKey(key);
try {
const result = await Promise.resolve(fnc());
coalesce({ key, result });
return result;
} catch (error) {
coalesce({ key, error });
throw error;
}
}
return enqueue(key);
}
// src/hash.ts
var crypto = __toESM(require("crypto"), 1);
var HashAlgorithm = /* @__PURE__ */ ((HashAlgorithm2) => {
HashAlgorithm2["SHA256"] = "sha256";
HashAlgorithm2["SHA512"] = "sha512";
HashAlgorithm2["MD5"] = "md5";
HashAlgorithm2["DJB2"] = "djb2";
return HashAlgorithm2;
})(HashAlgorithm || {});
function hash(object, algorithm = "sha256" /* SHA256 */) {
const objectString = JSON.stringify(object);
if (algorithm === "djb2" /* DJB2 */) {
return djb2Hash(objectString);
}
if (!crypto.getHashes().includes(algorithm)) {
throw new Error(`Unsupported hash algorithm: '${algorithm}'`);
}
const hasher = crypto.createHash(algorithm);
hasher.update(objectString);
return hasher.digest("hex");
}
function hashToNumber(object, min = 0, max = 10, algorithm = "sha256" /* SHA256 */) {
const hashResult = hash(object, algorithm);
const hashNumber = Number.parseInt(hashResult, 16);
const range = max - min + 1;
const result = min + hashNumber % range;
if (result < min) {
return min;
}
if (result > max) {
return max;
}
return result;
}
function djb2Hash(string_) {
let hash2 = 5381;
for (let i = 0; i < string_.length; i++) {
hash2 = hash2 * 33 ^ string_.charCodeAt(i);
}
return hash2.toString();
}
// src/sleep.ts
var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// src/stats.ts
var Stats = class {
_hits = 0;
_misses = 0;
_gets = 0;
_sets = 0;
_deletes = 0;
_clears = 0;
_vsize = 0;
_ksize = 0;
_count = 0;
_enabled = false;
constructor(options) {
if (options?.enabled) {
this._enabled = options.enabled;
}
}
/**
* @returns {boolean} - Whether the stats are enabled
*/
get enabled() {
return this._enabled;
}
/**
* @param {boolean} enabled - Whether to enable the stats
*/
set enabled(enabled) {
this._enabled = enabled;
}
/**
* @returns {number} - The number of hits
* @readonly
*/
get hits() {
return this._hits;
}
/**
* @returns {number} - The number of misses
* @readonly
*/
get misses() {
return this._misses;
}
/**
* @returns {number} - The number of gets
* @readonly
*/
get gets() {
return this._gets;
}
/**
* @returns {number} - The number of sets
* @readonly
*/
get sets() {
return this._sets;
}
/**
* @returns {number} - The number of deletes
* @readonly
*/
get deletes() {
return this._deletes;
}
/**
* @returns {number} - The number of clears
* @readonly
*/
get clears() {
return this._clears;
}
/**
* @returns {number} - The vsize (value size) of the cache instance
* @readonly
*/
get vsize() {
return this._vsize;
}
/**
* @returns {number} - The ksize (key size) of the cache instance
* @readonly
*/
get ksize() {
return this._ksize;
}
/**
* @returns {number} - The count of the cache instance
* @readonly
*/
get count() {
return this._count;
}
incrementHits() {
if (!this._enabled) {
return;
}
this._hits++;
}
incrementMisses() {
if (!this._enabled) {
return;
}
this._misses++;
}
incrementGets() {
if (!this._enabled) {
return;
}
this._gets++;
}
incrementSets() {
if (!this._enabled) {
return;
}
this._sets++;
}
incrementDeletes() {
if (!this._enabled) {
return;
}
this._deletes++;
}
incrementClears() {
if (!this._enabled) {
return;
}
this._clears++;
}
incrementVSize(value) {
if (!this._enabled) {
return;
}
this._vsize += this.roughSizeOfObject(value);
}
decreaseVSize(value) {
if (!this._enabled) {
return;
}
this._vsize -= this.roughSizeOfObject(value);
}
incrementKSize(key) {
if (!this._enabled) {
return;
}
this._ksize += this.roughSizeOfString(key);
}
decreaseKSize(key) {
if (!this._enabled) {
return;
}
this._ksize -= this.roughSizeOfString(key);
}
incrementCount() {
if (!this._enabled) {
return;
}
this._count++;
}
decreaseCount() {
if (!this._enabled) {
return;
}
this._count--;
}
setCount(count) {
if (!this._enabled) {
return;
}
this._count = count;
}
roughSizeOfString(value) {
return value.length * 2;
}
roughSizeOfObject(object) {
const objectList = [];
const stack = [object];
let bytes = 0;
while (stack.length > 0) {
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" && value !== null && !objectList.includes(value)) {
objectList.push(value);
for (const key in value) {
bytes += key.length * 2;
stack.push(value[key]);
}
}
}
return bytes;
}
reset() {
this._hits = 0;
this._misses = 0;
this._gets = 0;
this._sets = 0;
this._deletes = 0;
this._clears = 0;
this._vsize = 0;
this._ksize = 0;
this._count = 0;
}
resetStoreValues() {
this._vsize = 0;
this._ksize = 0;
this._count = 0;
}
};
// src/ttl.ts
function getTtlFromExpires(expires) {
if (expires === void 0 || expires === null) {
return void 0;
}
const now = Date.now();
if (expires < now) {
return void 0;
}
return expires - now;
}
function getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl) {
return secondaryTtl ?? primaryTtl ?? shorthandToMilliseconds(cacheableTtl);
}
function calculateTtlFromExpiration(ttl, expires) {
const ttlFromExpires = getTtlFromExpires(expires);
const expiresFromTtl = ttl ? Date.now() + ttl : void 0;
if (ttlFromExpires === void 0) {
return ttl;
}
if (expiresFromTtl === void 0) {
return ttlFromExpires;
}
if (expires && expires > expiresFromTtl) {
return ttl;
}
return ttlFromExpires;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
HashAlgorithm,
Stats,
calculateTtlFromExpiration,
coalesceAsync,
getCascadingTtl,
getTtlFromExpires,
hash,
hashToNumber,
shorthandToMilliseconds,
shorthandToTime,
sleep
});
"use strict";var P=Object.create;var l=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var j=(t,e)=>{for(var r in e)l(t,r,{get:e[r],enumerable:!0})},p=(t,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of N(e))!H.call(t,n)&&n!==r&&l(t,n,{get:()=>e[n],enumerable:!(i=C(e,n))||i.enumerable});return t};var E=(t,e,r)=>(r=t!=null?P(A(t)):{},p(e||!t||!t.__esModule?l(r,"default",{value:t,enumerable:!0}):r,t)),F=t=>p(l({},"__esModule",{value:!0}),t);var V={};j(V,{HashAlgorithm:()=>b,Stats:()=>m,calculateTtlFromExpiration:()=>O,coalesceAsync:()=>y,getCascadingTtl:()=>w,getTtlFromExpires:()=>d,hash:()=>f,hashToNumber:()=>v,isObject:()=>x,lessThan:()=>z,runIfFn:()=>S,shorthandToMilliseconds:()=>u,shorthandToTime:()=>g,sleep:()=>k});module.exports=F(V);var u=t=>{let e;if(t!==void 0){if(typeof t=="number")e=t;else if(typeof t=="string")if(t=t.trim(),Number.isNaN(Number(t))){let r=/^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(t);if(!r)throw new Error(`Unsupported time format: "${t}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`);let[,i,n]=r,s=Number.parseFloat(i);switch(n.toLowerCase()){case"ms":{e=s;break}case"s":{e=s*1e3;break}case"m":{e=s*1e3*60;break}case"h":{e=s*1e3*60*60;break}case"hr":{e=s*1e3*60*60;break}case"d":{e=s*1e3*60*60*24;break}default:e=Number(t)}}else e=Number(t);else throw new TypeError("Time must be a string or a number.");return e}},g=(t,e)=>{e??=new Date;let r=u(t);return r===void 0?e.getTime():e.getTime()+r};var a=new Map;function K(t){return a.has(t)}function D(t){a.set(t,[])}function I(t){a.delete(t)}function J(t,e){let r=T(t);r.push(e),a.set(t,r)}function T(t){return a.get(t)??[]}async function L(t){return new Promise((e,r)=>{J(t,{resolve:e,reject:r})})}function M(t){let e=T(t);return I(t),e}function _(t){let{key:e,error:r,result:i}=t;for(let n of M(e))r?n.reject(r):n.resolve(i)}async function y(t,e){if(!K(t)){D(t);try{let r=await Promise.resolve(e());return _({key:t,result:r}),r}catch(r){throw _({key:t,error:r}),r}}return L(t)}var c=E(require("crypto"),1),b=(n=>(n.SHA256="sha256",n.SHA512="sha512",n.MD5="md5",n.DJB2="djb2",n))(b||{});function f(t,e={algorithm:"sha256",serialize:JSON.stringify}){e?.algorithm||(e.algorithm="sha256"),e?.serialize||(e.serialize=JSON.stringify);let r=e.serialize(t);if(e?.algorithm==="djb2")return $(r);if(!c.getHashes().includes(e.algorithm))throw new Error(`Unsupported hash algorithm: '${e?.algorithm}'`);let i=c.createHash(e.algorithm);return i.update(r),i.digest("hex")}function v(t,e={min:0,max:10,algorithm:"sha256",serialize:JSON.stringify}){let r=e?.min??0,i=e?.max??10;if(r>=i)throw new Error(`Invalid range: min (${r}) must be less than max (${i})`);e?.algorithm||(e.algorithm="sha256"),e?.serialize||(e.serialize=JSON.stringify);let n=f(t,e),s=Number.parseInt(n,16),o=i-r+1,h=r+s%o;return h<r?r:h>i?i:h}function $(t){let e=5381;for(let r=0;r<t.length;r++)e=e*33^t.charCodeAt(r);return e.toString()}function x(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function z(t,e){return typeof t=="number"&&typeof e=="number"?t<e:!1}function S(t,...e){return typeof t=="function"?t(...e):t}var k=async t=>new Promise(e=>setTimeout(e,t));var m=class{_hits=0;_misses=0;_gets=0;_sets=0;_deletes=0;_clears=0;_vsize=0;_ksize=0;_count=0;_enabled=!1;constructor(e){e?.enabled&&(this._enabled=e.enabled)}get enabled(){return this._enabled}set enabled(e){this._enabled=e}get hits(){return this._hits}get misses(){return this._misses}get gets(){return this._gets}get sets(){return this._sets}get deletes(){return this._deletes}get clears(){return this._clears}get vsize(){return this._vsize}get ksize(){return this._ksize}get count(){return this._count}incrementHits(){this._enabled&&this._hits++}incrementMisses(){this._enabled&&this._misses++}incrementGets(){this._enabled&&this._gets++}incrementSets(){this._enabled&&this._sets++}incrementDeletes(){this._enabled&&this._deletes++}incrementClears(){this._enabled&&this._clears++}incrementVSize(e){this._enabled&&(this._vsize+=this.roughSizeOfObject(e))}decreaseVSize(e){this._enabled&&(this._vsize-=this.roughSizeOfObject(e))}incrementKSize(e){this._enabled&&(this._ksize+=this.roughSizeOfString(e))}decreaseKSize(e){this._enabled&&(this._ksize-=this.roughSizeOfString(e))}incrementCount(){this._enabled&&this._count++}decreaseCount(){this._enabled&&this._count--}setCount(e){this._enabled&&(this._count=e)}roughSizeOfString(e){return e.length*2}roughSizeOfObject(e){let r=[],i=[e],n=0;for(;i.length>0;){let s=i.pop();if(typeof s=="boolean")n+=4;else if(typeof s=="string")n+=s.length*2;else if(typeof s=="number")n+=8;else if(typeof s=="object"&&s!==null&&!r.includes(s)){r.push(s);for(let o in s)n+=o.length*2,i.push(s[o])}}return n}reset(){this._hits=0,this._misses=0,this._gets=0,this._sets=0,this._deletes=0,this._clears=0,this._vsize=0,this._ksize=0,this._count=0}resetStoreValues(){this._vsize=0,this._ksize=0,this._count=0}};function d(t){if(t==null)return;let e=Date.now();if(!(t<e))return t-e}function w(t,e,r){return r??e??u(t)}function O(t,e){let r=d(e),i=t?Date.now()+t:void 0;return r===void 0?t:i===void 0?r:e&&e>i?t:r}0&&(module.exports={HashAlgorithm,Stats,calculateTtlFromExpiration,coalesceAsync,getCascadingTtl,getTtlFromExpires,hash,hashToNumber,isObject,lessThan,runIfFn,shorthandToMilliseconds,shorthandToTime,sleep});
+19
-4

@@ -74,11 +74,26 @@ /**

}
type HashOptions = {
algorithm?: HashAlgorithm;
serialize?: (object: any) => string;
};
type hashToNumberOptions = HashOptions & {
min?: number;
max?: number;
};
/**
* Hashes an object using the specified algorithm. The default algorithm is 'sha256'.
* @param object The object to hash
* @param algorithm The hash algorithm to use
* @param options The hash options to use
* @returns {string} The hash of the object
*/
declare function hash(object: any, algorithm?: HashAlgorithm): string;
declare function hashToNumber(object: any, min?: number, max?: number, algorithm?: HashAlgorithm): number;
declare function hash(object: any, options?: HashOptions): string;
declare function hashToNumber(object: any, options?: hashToNumberOptions): number;
declare function isObject<T = Record<string, unknown>>(value: unknown): value is T;
declare function lessThan(number1?: number, number2?: number): boolean;
type Function_<P, T> = (...arguments_: P[]) => T;
declare function runIfFn<T, P>(valueOrFunction: T | Function_<P, T>, ...arguments_: P[]): T;
declare const sleep: (ms: number) => Promise<unknown>;

@@ -196,2 +211,2 @@

export { type CacheableItem, type CacheableStoreItem, HashAlgorithm, Stats, type StatsOptions, calculateTtlFromExpiration, coalesceAsync, getCascadingTtl, getTtlFromExpires, hash, hashToNumber, shorthandToMilliseconds, shorthandToTime, sleep };
export { type CacheableItem, type CacheableStoreItem, HashAlgorithm, Stats, type StatsOptions, calculateTtlFromExpiration, coalesceAsync, getCascadingTtl, getTtlFromExpires, hash, hashToNumber, isObject, lessThan, runIfFn, shorthandToMilliseconds, shorthandToTime, sleep };

@@ -74,11 +74,26 @@ /**

}
type HashOptions = {
algorithm?: HashAlgorithm;
serialize?: (object: any) => string;
};
type hashToNumberOptions = HashOptions & {
min?: number;
max?: number;
};
/**
* Hashes an object using the specified algorithm. The default algorithm is 'sha256'.
* @param object The object to hash
* @param algorithm The hash algorithm to use
* @param options The hash options to use
* @returns {string} The hash of the object
*/
declare function hash(object: any, algorithm?: HashAlgorithm): string;
declare function hashToNumber(object: any, min?: number, max?: number, algorithm?: HashAlgorithm): number;
declare function hash(object: any, options?: HashOptions): string;
declare function hashToNumber(object: any, options?: hashToNumberOptions): number;
declare function isObject<T = Record<string, unknown>>(value: unknown): value is T;
declare function lessThan(number1?: number, number2?: number): boolean;
type Function_<P, T> = (...arguments_: P[]) => T;
declare function runIfFn<T, P>(valueOrFunction: T | Function_<P, T>, ...arguments_: P[]): T;
declare const sleep: (ms: number) => Promise<unknown>;

@@ -196,2 +211,2 @@

export { type CacheableItem, type CacheableStoreItem, HashAlgorithm, Stats, type StatsOptions, calculateTtlFromExpiration, coalesceAsync, getCascadingTtl, getTtlFromExpires, hash, hashToNumber, shorthandToMilliseconds, shorthandToTime, sleep };
export { type CacheableItem, type CacheableStoreItem, HashAlgorithm, Stats, type StatsOptions, calculateTtlFromExpiration, coalesceAsync, getCascadingTtl, getTtlFromExpires, hash, hashToNumber, isObject, lessThan, runIfFn, shorthandToMilliseconds, shorthandToTime, sleep };

@@ -1,421 +0,1 @@

// src/shorthand-time.ts
var shorthandToMilliseconds = (shorthand) => {
let milliseconds;
if (shorthand === void 0) {
return void 0;
}
if (typeof shorthand === "number") {
milliseconds = shorthand;
} else if (typeof shorthand === "string") {
shorthand = shorthand.trim();
if (Number.isNaN(Number(shorthand))) {
const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand);
if (!match) {
throw new Error(
`Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`
);
}
const [, value, unit] = match;
const numericValue = Number.parseFloat(value);
const unitLower = unit.toLowerCase();
switch (unitLower) {
case "ms": {
milliseconds = numericValue;
break;
}
case "s": {
milliseconds = numericValue * 1e3;
break;
}
case "m": {
milliseconds = numericValue * 1e3 * 60;
break;
}
case "h": {
milliseconds = numericValue * 1e3 * 60 * 60;
break;
}
case "hr": {
milliseconds = numericValue * 1e3 * 60 * 60;
break;
}
case "d": {
milliseconds = numericValue * 1e3 * 60 * 60 * 24;
break;
}
/* c8 ignore next 3 */
default: {
milliseconds = Number(shorthand);
}
}
} else {
milliseconds = Number(shorthand);
}
} else {
throw new TypeError("Time must be a string or a number.");
}
return milliseconds;
};
var shorthandToTime = (shorthand, fromDate) => {
fromDate ??= /* @__PURE__ */ new Date();
const milliseconds = shorthandToMilliseconds(shorthand);
if (milliseconds === void 0) {
return fromDate.getTime();
}
return fromDate.getTime() + milliseconds;
};
// src/coalesce-async.ts
var callbacks = /* @__PURE__ */ new Map();
function hasKey(key) {
return callbacks.has(key);
}
function addKey(key) {
callbacks.set(key, []);
}
function removeKey(key) {
callbacks.delete(key);
}
function addCallbackToKey(key, callback) {
const stash = getCallbacksByKey(key);
stash.push(callback);
callbacks.set(key, stash);
}
function getCallbacksByKey(key) {
return callbacks.get(key) ?? [];
}
async function enqueue(key) {
return new Promise((resolve, reject) => {
const callback = { resolve, reject };
addCallbackToKey(key, callback);
});
}
function dequeue(key) {
const stash = getCallbacksByKey(key);
removeKey(key);
return stash;
}
function coalesce(options) {
const { key, error, result } = options;
for (const callback of dequeue(key)) {
if (error) {
callback.reject(error);
} else {
callback.resolve(result);
}
}
}
async function coalesceAsync(key, fnc) {
if (!hasKey(key)) {
addKey(key);
try {
const result = await Promise.resolve(fnc());
coalesce({ key, result });
return result;
} catch (error) {
coalesce({ key, error });
throw error;
}
}
return enqueue(key);
}
// src/hash.ts
import * as crypto from "crypto";
var HashAlgorithm = /* @__PURE__ */ ((HashAlgorithm2) => {
HashAlgorithm2["SHA256"] = "sha256";
HashAlgorithm2["SHA512"] = "sha512";
HashAlgorithm2["MD5"] = "md5";
HashAlgorithm2["DJB2"] = "djb2";
return HashAlgorithm2;
})(HashAlgorithm || {});
function hash(object, algorithm = "sha256" /* SHA256 */) {
const objectString = JSON.stringify(object);
if (algorithm === "djb2" /* DJB2 */) {
return djb2Hash(objectString);
}
if (!crypto.getHashes().includes(algorithm)) {
throw new Error(`Unsupported hash algorithm: '${algorithm}'`);
}
const hasher = crypto.createHash(algorithm);
hasher.update(objectString);
return hasher.digest("hex");
}
function hashToNumber(object, min = 0, max = 10, algorithm = "sha256" /* SHA256 */) {
const hashResult = hash(object, algorithm);
const hashNumber = Number.parseInt(hashResult, 16);
const range = max - min + 1;
const result = min + hashNumber % range;
if (result < min) {
return min;
}
if (result > max) {
return max;
}
return result;
}
function djb2Hash(string_) {
let hash2 = 5381;
for (let i = 0; i < string_.length; i++) {
hash2 = hash2 * 33 ^ string_.charCodeAt(i);
}
return hash2.toString();
}
// src/sleep.ts
var sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// src/stats.ts
var Stats = class {
_hits = 0;
_misses = 0;
_gets = 0;
_sets = 0;
_deletes = 0;
_clears = 0;
_vsize = 0;
_ksize = 0;
_count = 0;
_enabled = false;
constructor(options) {
if (options?.enabled) {
this._enabled = options.enabled;
}
}
/**
* @returns {boolean} - Whether the stats are enabled
*/
get enabled() {
return this._enabled;
}
/**
* @param {boolean} enabled - Whether to enable the stats
*/
set enabled(enabled) {
this._enabled = enabled;
}
/**
* @returns {number} - The number of hits
* @readonly
*/
get hits() {
return this._hits;
}
/**
* @returns {number} - The number of misses
* @readonly
*/
get misses() {
return this._misses;
}
/**
* @returns {number} - The number of gets
* @readonly
*/
get gets() {
return this._gets;
}
/**
* @returns {number} - The number of sets
* @readonly
*/
get sets() {
return this._sets;
}
/**
* @returns {number} - The number of deletes
* @readonly
*/
get deletes() {
return this._deletes;
}
/**
* @returns {number} - The number of clears
* @readonly
*/
get clears() {
return this._clears;
}
/**
* @returns {number} - The vsize (value size) of the cache instance
* @readonly
*/
get vsize() {
return this._vsize;
}
/**
* @returns {number} - The ksize (key size) of the cache instance
* @readonly
*/
get ksize() {
return this._ksize;
}
/**
* @returns {number} - The count of the cache instance
* @readonly
*/
get count() {
return this._count;
}
incrementHits() {
if (!this._enabled) {
return;
}
this._hits++;
}
incrementMisses() {
if (!this._enabled) {
return;
}
this._misses++;
}
incrementGets() {
if (!this._enabled) {
return;
}
this._gets++;
}
incrementSets() {
if (!this._enabled) {
return;
}
this._sets++;
}
incrementDeletes() {
if (!this._enabled) {
return;
}
this._deletes++;
}
incrementClears() {
if (!this._enabled) {
return;
}
this._clears++;
}
incrementVSize(value) {
if (!this._enabled) {
return;
}
this._vsize += this.roughSizeOfObject(value);
}
decreaseVSize(value) {
if (!this._enabled) {
return;
}
this._vsize -= this.roughSizeOfObject(value);
}
incrementKSize(key) {
if (!this._enabled) {
return;
}
this._ksize += this.roughSizeOfString(key);
}
decreaseKSize(key) {
if (!this._enabled) {
return;
}
this._ksize -= this.roughSizeOfString(key);
}
incrementCount() {
if (!this._enabled) {
return;
}
this._count++;
}
decreaseCount() {
if (!this._enabled) {
return;
}
this._count--;
}
setCount(count) {
if (!this._enabled) {
return;
}
this._count = count;
}
roughSizeOfString(value) {
return value.length * 2;
}
roughSizeOfObject(object) {
const objectList = [];
const stack = [object];
let bytes = 0;
while (stack.length > 0) {
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" && value !== null && !objectList.includes(value)) {
objectList.push(value);
for (const key in value) {
bytes += key.length * 2;
stack.push(value[key]);
}
}
}
return bytes;
}
reset() {
this._hits = 0;
this._misses = 0;
this._gets = 0;
this._sets = 0;
this._deletes = 0;
this._clears = 0;
this._vsize = 0;
this._ksize = 0;
this._count = 0;
}
resetStoreValues() {
this._vsize = 0;
this._ksize = 0;
this._count = 0;
}
};
// src/ttl.ts
function getTtlFromExpires(expires) {
if (expires === void 0 || expires === null) {
return void 0;
}
const now = Date.now();
if (expires < now) {
return void 0;
}
return expires - now;
}
function getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl) {
return secondaryTtl ?? primaryTtl ?? shorthandToMilliseconds(cacheableTtl);
}
function calculateTtlFromExpiration(ttl, expires) {
const ttlFromExpires = getTtlFromExpires(expires);
const expiresFromTtl = ttl ? Date.now() + ttl : void 0;
if (ttlFromExpires === void 0) {
return ttl;
}
if (expiresFromTtl === void 0) {
return ttlFromExpires;
}
if (expires && expires > expiresFromTtl) {
return ttl;
}
return ttlFromExpires;
}
export {
HashAlgorithm,
Stats,
calculateTtlFromExpiration,
coalesceAsync,
getCascadingTtl,
getTtlFromExpires,
hash,
hashToNumber,
shorthandToMilliseconds,
shorthandToTime,
sleep
};
var a=t=>{let e;if(t!==void 0){if(typeof t=="number")e=t;else if(typeof t=="string")if(t=t.trim(),Number.isNaN(Number(t))){let r=/^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(t);if(!r)throw new Error(`Unsupported time format: "${t}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.`);let[,i,s]=r,n=Number.parseFloat(i);switch(s.toLowerCase()){case"ms":{e=n;break}case"s":{e=n*1e3;break}case"m":{e=n*1e3*60;break}case"h":{e=n*1e3*60*60;break}case"hr":{e=n*1e3*60*60;break}case"d":{e=n*1e3*60*60*24;break}default:e=Number(t)}}else e=Number(t);else throw new TypeError("Time must be a string or a number.");return e}},g=(t,e)=>{e??=new Date;let r=a(t);return r===void 0?e.getTime():e.getTime()+r};var u=new Map;function _(t){return u.has(t)}function T(t){u.set(t,[])}function y(t){u.delete(t)}function v(t,e){let r=b(t);r.push(e),u.set(t,r)}function b(t){return u.get(t)??[]}async function x(t){return new Promise((e,r)=>{v(t,{resolve:e,reject:r})})}function z(t){let e=b(t);return y(t),e}function h(t){let{key:e,error:r,result:i}=t;for(let s of z(e))r?s.reject(r):s.resolve(i)}async function S(t,e){if(!_(t)){T(t);try{let r=await Promise.resolve(e());return h({key:t,result:r}),r}catch(r){throw h({key:t,error:r}),r}}return x(t)}import*as l from"crypto";var f=(s=>(s.SHA256="sha256",s.SHA512="sha512",s.MD5="md5",s.DJB2="djb2",s))(f||{});function d(t,e={algorithm:"sha256",serialize:JSON.stringify}){e?.algorithm||(e.algorithm="sha256"),e?.serialize||(e.serialize=JSON.stringify);let r=e.serialize(t);if(e?.algorithm==="djb2")return w(r);if(!l.getHashes().includes(e.algorithm))throw new Error(`Unsupported hash algorithm: '${e?.algorithm}'`);let i=l.createHash(e.algorithm);return i.update(r),i.digest("hex")}function k(t,e={min:0,max:10,algorithm:"sha256",serialize:JSON.stringify}){let r=e?.min??0,i=e?.max??10;if(r>=i)throw new Error(`Invalid range: min (${r}) must be less than max (${i})`);e?.algorithm||(e.algorithm="sha256"),e?.serialize||(e.serialize=JSON.stringify);let s=d(t,e),n=Number.parseInt(s,16),o=i-r+1,c=r+n%o;return c<r?r:c>i?i:c}function w(t){let e=5381;for(let r=0;r<t.length;r++)e=e*33^t.charCodeAt(r);return e.toString()}function O(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function P(t,e){return typeof t=="number"&&typeof e=="number"?t<e:!1}function C(t,...e){return typeof t=="function"?t(...e):t}var N=async t=>new Promise(e=>setTimeout(e,t));var m=class{_hits=0;_misses=0;_gets=0;_sets=0;_deletes=0;_clears=0;_vsize=0;_ksize=0;_count=0;_enabled=!1;constructor(e){e?.enabled&&(this._enabled=e.enabled)}get enabled(){return this._enabled}set enabled(e){this._enabled=e}get hits(){return this._hits}get misses(){return this._misses}get gets(){return this._gets}get sets(){return this._sets}get deletes(){return this._deletes}get clears(){return this._clears}get vsize(){return this._vsize}get ksize(){return this._ksize}get count(){return this._count}incrementHits(){this._enabled&&this._hits++}incrementMisses(){this._enabled&&this._misses++}incrementGets(){this._enabled&&this._gets++}incrementSets(){this._enabled&&this._sets++}incrementDeletes(){this._enabled&&this._deletes++}incrementClears(){this._enabled&&this._clears++}incrementVSize(e){this._enabled&&(this._vsize+=this.roughSizeOfObject(e))}decreaseVSize(e){this._enabled&&(this._vsize-=this.roughSizeOfObject(e))}incrementKSize(e){this._enabled&&(this._ksize+=this.roughSizeOfString(e))}decreaseKSize(e){this._enabled&&(this._ksize-=this.roughSizeOfString(e))}incrementCount(){this._enabled&&this._count++}decreaseCount(){this._enabled&&this._count--}setCount(e){this._enabled&&(this._count=e)}roughSizeOfString(e){return e.length*2}roughSizeOfObject(e){let r=[],i=[e],s=0;for(;i.length>0;){let n=i.pop();if(typeof n=="boolean")s+=4;else if(typeof n=="string")s+=n.length*2;else if(typeof n=="number")s+=8;else if(typeof n=="object"&&n!==null&&!r.includes(n)){r.push(n);for(let o in n)s+=o.length*2,i.push(n[o])}}return s}reset(){this._hits=0,this._misses=0,this._gets=0,this._sets=0,this._deletes=0,this._clears=0,this._vsize=0,this._ksize=0,this._count=0}resetStoreValues(){this._vsize=0,this._ksize=0,this._count=0}};function p(t){if(t==null)return;let e=Date.now();if(!(t<e))return t-e}function A(t,e,r){return r??e??a(t)}function H(t,e){let r=p(e),i=t?Date.now()+t:void 0;return r===void 0?t:i===void 0?r:e&&e>i?t:r}export{f as HashAlgorithm,m as Stats,H as calculateTtlFromExpiration,S as coalesceAsync,A as getCascadingTtl,p as getTtlFromExpires,d as hash,k as hashToNumber,O as isObject,P as lessThan,C as runIfFn,a as shorthandToMilliseconds,g as shorthandToTime,N as sleep};
{
"name": "@cacheable/utils",
"version": "1.1.1",
"version": "2.0.0",
"description": "Cacheable Utilities for Caching Libraries",

@@ -49,3 +49,3 @@ "type": "module",

"scripts": {
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean --minify",
"prepublish": "pnpm build",

@@ -52,0 +52,0 @@ "lint": "biome check --write --error-on-warnings",

+140
-0

@@ -29,2 +29,5 @@ [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)

* [Time to Live (TTL) Helpers](#time-to-live-ttl-helpers)
* [Run if Function Helper](#run-if-function-helper)
* [Less Than Helper](#less-than-helper)
* [Is Object Helper](#is-object-helper)
* [How to Contribute](#how-to-contribute)

@@ -199,2 +202,139 @@ * [License and Copyright](#license-and-copyright)

# Run if Function Helper
The `runIfFn` utility function provides a convenient way to conditionally execute functions or return values based on whether the input is a function or not. This pattern is commonly used in UI libraries and configuration systems where values can be either static or computed.
```typescript
import { runIfFn } from '@cacheable/utils';
// Static value - returns the value as-is
const staticValue = runIfFn('hello world');
console.log(staticValue); // 'hello world'
// Function with no arguments - executes the function
const dynamicValue = runIfFn(() => new Date().toISOString());
console.log(dynamicValue); // Current timestamp
// Function with arguments - executes with provided arguments
const sum = runIfFn((a: number, b: number) => a + b, 5, 10);
console.log(sum); // 15
// Complex example with conditional logic
const getConfig = (isDevelopment: boolean) => ({
apiUrl: isDevelopment ? 'http://localhost:3000' : 'https://api.example.com',
timeout: isDevelopment ? 5000 : 30000
});
const config = runIfFn(getConfig, true);
console.log(config); // { apiUrl: 'http://localhost:3000', timeout: 5000 }
```
# Less Than Helper
The `lessThan` utility function provides a safe way to compare two values and determine if the first value is less than the second. It only performs the comparison if both values are valid numbers, returning `false` for any non-number inputs.
```typescript
import { lessThan } from '@cacheable/utils';
// Basic number comparisons
console.log(lessThan(1, 2)); // true
console.log(lessThan(2, 1)); // false
console.log(lessThan(1, 1)); // false
// Works with negative numbers
console.log(lessThan(-1, 0)); // true
console.log(lessThan(-2, -1)); // true
// Works with decimal numbers
console.log(lessThan(1.5, 2.5)); // true
console.log(lessThan(2.7, 2.7)); // false
// Safe handling of non-number values
console.log(lessThan("1", 2)); // false
console.log(lessThan(1, "2")); // false
console.log(lessThan(null, 1)); // false
console.log(lessThan(undefined, 1)); // false
console.log(lessThan(NaN, 1)); // false
// Useful in filtering and sorting operations
const numbers = [5, 2, 8, 1, 9];
const lessThanFive = numbers.filter(n => lessThan(n, 5));
console.log(lessThanFive); // [2, 1]
// Safe comparison in conditional logic
function processValue(a?: number, b?: number) {
if (lessThan(a, b)) {
return `${a} is less than ${b}`;
}
return 'Invalid comparison or a >= b';
}
```
This utility is particularly useful when dealing with potentially undefined or invalid numeric values, ensuring type safety in comparison operations.
# Is Object Helper
The `isObject` utility function provides a type-safe way to determine if a value is a plain object. It returns `true` for objects but `false` for arrays, `null`, functions, and primitive types. This function also serves as a TypeScript type guard.
```typescript
import { isObject } from '@cacheable/utils';
// Basic object detection
console.log(isObject({})); // true
console.log(isObject({ name: 'John', age: 30 })); // true
console.log(isObject(Object.create(null))); // true
// Arrays are not considered objects
console.log(isObject([])); // false
console.log(isObject([1, 2, 3])); // false
// null is not considered an object (despite typeof null === 'object')
console.log(isObject(null)); // false
// Primitive types return false
console.log(isObject('string')); // false
console.log(isObject(123)); // false
console.log(isObject(true)); // false
console.log(isObject(undefined)); // false
// Functions return false
console.log(isObject(() => {})); // false
console.log(isObject(Date)); // false
// Built-in object types return true
console.log(isObject(new Date())); // true
console.log(isObject(/regex/)); // true
console.log(isObject(new Error('test'))); // true
console.log(isObject(new Map())); // true
// TypeScript type guard usage
function processValue(value: unknown) {
if (isObject<{ name: string; age: number }>(value)) {
// TypeScript now knows value is an object with name and age properties
console.log(`Name: ${value.name}, Age: ${value.age}`);
}
}
// Useful for configuration validation
function validateConfig(config: unknown) {
if (!isObject(config)) {
throw new Error('Configuration must be an object');
}
// Safe to access object properties
return config;
}
// Filtering arrays for objects only
const mixedArray = [1, 'string', {}, [], null, { valid: true }];
const objectsOnly = mixedArray.filter(isObject);
console.log(objectsOnly); // [{}', { valid: true }]
```
This utility is particularly useful for:
- **Type validation** - Ensuring values are objects before accessing properties
- **TypeScript type guarding** - Narrowing types in conditional blocks
- **Configuration parsing** - Validating that configuration values are objects
- **Data filtering** - Separating objects from other data types
# How to Contribute

@@ -201,0 +341,0 @@