@cacheable/utils
Advanced tools
+1
-468
@@ -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 }; |
+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 }; |
+1
-421
@@ -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}; |
+2
-2
| { | ||
| "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 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
344
68.63%40071
-10.6%239
-77.56%3
200%1
Infinity%