"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 */
