blob: e129983603346a4ff7a4624d7ee07779a696fb93 [file]
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __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,
createWrapKey: () => createWrapKey,
getCascadingTtl: () => getCascadingTtl,
getOrSet: () => getOrSet,
getTtlFromExpires: () => getTtlFromExpires,
hash: () => hash,
hashSync: () => hashSync,
hashToNumber: () => hashToNumber,
hashToNumberSync: () => hashToNumberSync,
isKeyvInstance: () => isKeyvInstance,
isObject: () => isObject,
lessThan: () => lessThan,
runIfFn: () => runIfFn,
shorthandToMilliseconds: () => shorthandToMilliseconds,
shorthandToTime: () => shorthandToTime,
sleep: () => sleep,
wrap: () => wrap,
wrapSync: () => wrapSync
});
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") {
return void 0;
}
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;
}
/* v8 ignore next -- @preserve */
default: {
milliseconds = Number(shorthand);
}
}
} else {
milliseconds = Number(shorthand);
}
}
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 import_hashery = require("hashery");
var HashAlgorithm = /* @__PURE__ */ ((HashAlgorithm2) => {
HashAlgorithm2["SHA256"] = "SHA-256";
HashAlgorithm2["SHA384"] = "SHA-384";
HashAlgorithm2["SHA512"] = "SHA-512";
HashAlgorithm2["DJB2"] = "djb2";
HashAlgorithm2["FNV1"] = "fnv1";
HashAlgorithm2["MURMER"] = "murmer";
HashAlgorithm2["CRC32"] = "crc32";
return HashAlgorithm2;
})(HashAlgorithm || {});
async function hash(object, options = {
algorithm: "SHA-256" /* SHA256 */,
serialize: JSON.stringify
}) {
const algorithm = options?.algorithm ?? "SHA-256" /* SHA256 */;
const serialize = options?.serialize ?? JSON.stringify;
const objectString = serialize(object);
const hashery = new import_hashery.Hashery();
return hashery.toHash(objectString, { algorithm });
}
function hashSync(object, options = {
algorithm: "djb2" /* DJB2 */,
serialize: JSON.stringify
}) {
const algorithm = options?.algorithm ?? "djb2" /* DJB2 */;
const serialize = options?.serialize ?? JSON.stringify;
const objectString = serialize(object);
const hashery = new import_hashery.Hashery();
return hashery.toHashSync(objectString, { algorithm });
}
async function hashToNumber(object, options = {
min: 0,
max: 10,
algorithm: "SHA-256" /* SHA256 */,
serialize: JSON.stringify
}) {
const min = options?.min ?? 0;
const max = options?.max ?? 10;
const algorithm = options?.algorithm ?? "SHA-256" /* SHA256 */;
const serialize = options?.serialize ?? JSON.stringify;
const hashLength = options?.hashLength ?? 16;
if (min >= max) {
throw new Error(
`Invalid range: min (${min}) must be less than max (${max})`
);
}
const objectString = serialize(object);
const hashery = new import_hashery.Hashery();
return hashery.toNumber(objectString, {
algorithm,
min,
max,
hashLength
});
}
function hashToNumberSync(object, options = {
min: 0,
max: 10,
algorithm: "djb2" /* DJB2 */,
serialize: JSON.stringify
}) {
const min = options?.min ?? 0;
const max = options?.max ?? 10;
const algorithm = options?.algorithm ?? "djb2" /* DJB2 */;
const serialize = options?.serialize ?? JSON.stringify;
const hashLength = options?.hashLength ?? 16;
if (min >= max) {
throw new Error(
`Invalid range: min (${min}) must be less than max (${max})`
);
}
const objectString = serialize(object);
const hashery = new import_hashery.Hashery();
return hashery.toNumberSync(objectString, {
algorithm,
min,
max,
hashLength
});
}
// src/is-keyv-instance.ts
var import_keyv = require("keyv");
function isKeyvInstance(keyv) {
if (keyv === null || keyv === void 0) {
return false;
}
if (keyv instanceof import_keyv.Keyv) {
return true;
}
const keyvMethods = [
"generateIterator",
"get",
"getMany",
"set",
"setMany",
"delete",
"deleteMany",
"has",
"hasMany",
"clear",
"disconnect",
"serialize",
"deserialize"
];
return keyvMethods.every((method) => typeof keyv[method] === "function");
}
// src/is-object.ts
function isObject(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
// src/less-than.ts
function lessThan(number1, number2) {
return typeof number1 === "number" && typeof number2 === "number" ? number1 < number2 : false;
}
// src/memoize.ts
function wrapSync(function_, options) {
const { ttl, keyPrefix, cache, serialize } = options;
return (...arguments_) => {
let cacheKey = createWrapKey(function_, arguments_, {
keyPrefix,
serialize
});
if (options.createKey) {
cacheKey = options.createKey(function_, arguments_, options);
}
let value = cache.get(cacheKey);
if (value === void 0) {
try {
value = function_(...arguments_);
cache.set(cacheKey, value, ttl);
} catch (error) {
cache.emit("error", error);
if (options.cacheErrors) {
cache.set(cacheKey, error, ttl);
}
}
}
return value;
};
}
async function getOrSet(key, function_, options) {
const keyString = typeof key === "function" ? key(options) : key;
let value;
try {
value = await options.cache.get(keyString);
} catch (error) {
options.cache.emit("error", error);
if (options.throwErrors === true || options.throwErrors === "store") {
throw error;
}
}
if (value === void 0) {
const cacheId = options.cacheId ?? "default";
const coalesceKey = `${cacheId}::${keyString}`;
value = await coalesceAsync(coalesceKey, async () => {
let result;
try {
try {
result = await function_();
} catch (error) {
throw new ErrorEnvelope(
error,
"function"
);
}
try {
await options.cache.set(keyString, result, options.ttl);
} catch (error) {
throw new ErrorEnvelope(error, "store");
}
return result;
} catch (caught) {
const errorType = caught instanceof ErrorEnvelope ? caught.context : (
/* c8 ignore next 1 */
void 0
);
const error = caught instanceof ErrorEnvelope ? caught.error : caught;
options.cache.emit("error", error);
if (options.cacheErrors) {
await options.cache.set(keyString, error, options.ttl);
}
if (options.throwErrors === true || options.throwErrors === errorType) {
throw error;
}
}
return result;
});
}
return value;
}
function wrap(function_, options) {
const { keyPrefix, serialize } = options;
return async (...arguments_) => {
let cacheKey = createWrapKey(function_, arguments_, {
keyPrefix,
serialize
});
if (options.createKey) {
cacheKey = options.createKey(function_, arguments_, options);
}
return getOrSet(
cacheKey,
async () => function_(...arguments_),
options
);
};
}
function createWrapKey(function_, arguments_, options) {
const { keyPrefix, serialize } = options || {};
if (!keyPrefix) {
return `${function_.name}::${hashSync(arguments_, { serialize })}`;
}
return `${keyPrefix}::${function_.name}::${hashSync(arguments_, { serialize })}`;
}
var ErrorEnvelope = class {
constructor(error, context) {
this.error = error;
this.context = context;
}
};
// src/run-if-fn.ts
function runIfFn(valueOrFunction, ...arguments_) {
return typeof valueOrFunction === "function" ? valueOrFunction(...arguments_) : valueOrFunction;
}
// 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 (value === null || value === void 0) {
bytes += 4;
continue;
}
if (objectList.includes(value)) {
continue;
}
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,
createWrapKey,
getCascadingTtl,
getOrSet,
getTtlFromExpires,
hash,
hashSync,
hashToNumber,
hashToNumberSync,
isKeyvInstance,
isObject,
lessThan,
runIfFn,
shorthandToMilliseconds,
shorthandToTime,
sleep,
wrap,
wrapSync
});
/* v8 ignore next -- @preserve */