{"version":3,"file":"index.mjs","names":[],"sources":["../src/serializer.ts","../src/setOptions.ts","../src/cache.ts","../src/time.ts"],"sourcesContent":["import superjson from 'superjson'\n\nexport const serialize = <T>(data: T): string => {\n  return superjson.stringify(data)\n}\n\nexport const deserialize = <T>(serialized: string | null): T | null => {\n  if (serialized === undefined || serialized === null) return serialized\n\n  return superjson.parse<T>(serialized)\n}\n","import type { SetOptions } from 'redis'\nimport type { Expiry } from './types'\n\n/**\n * Converts an Expiry value into Redis SetOptions.\n * @param {Expiry | undefined} expiry - The expiration time, either as a TTL in milliseconds or an exact Date.\n * @returns {SetOptions | undefined} Redis-compatible options, or undefined if no expiry.\n */\nexport const toSetOptions = (\n  expiry: Expiry | undefined\n): SetOptions | undefined => {\n  const options: SetOptions = {}\n\n  // Expiry has a value\n  if (expiry !== undefined) {\n    if (typeof expiry === 'number') {\n      options.PX = expiry // Store TTL in milliseconds\n    } else if (expiry instanceof Date && !Number.isNaN(expiry.getTime())) {\n      const timestamp = expiry.getTime()\n      if (timestamp > Date.now()) {\n        options.PXAT = timestamp // Use exact expiration time in milliseconds\n      } else {\n        options.PX = 1 // Past timestamps are invalid - expire immediately\n      }\n    }\n  }\n\n  // If expiry does not have a value, fall back to 1 sec\n  if (!options.PX && !options.PXAT) {\n    options.EX = 1\n  }\n\n  return options\n}\n","import type { IPersistor } from '@sebspark/memredis'\nimport { deserialize, serialize } from './serializer'\nimport { toSetOptions } from './setOptions'\nimport type { Cache, CachingOptions } from './types'\n\n/**\n * Creates a cache instance using a given persistor.\n * @param {IPersistor} persistor - The underlying storage for caching.\n * @param {string?} prefix - An optional prefix that will be prepended to the key, formatted as `prefix:key`.\n * @returns {Cache} A cache instance.\n */\nexport const createCache = (persistor: IPersistor, prefix?: string): Cache => {\n  // Tracks in-progress requests to prevent duplicate calls\n  const pendingPromises = new Map<string, Promise<unknown>>()\n\n  const cache: Cache = {\n    persistor,\n    wrap: <A extends unknown[], R>(\n      delegate: (...args: A) => Promise<R>,\n      options: CachingOptions<A, R>\n    ): ((...args: A) => Promise<R>) => {\n      return async (...args: A): Promise<R> => {\n        // Compute key\n        let key =\n          typeof options.key === 'string' ? options.key : options.key(...args)\n\n        // Apply prefix if provided\n        if (prefix) {\n          key = `${prefix}:${key}`\n        }\n\n        // Return the pending request if one exists\n        if (pendingPromises.has(key)) {\n          return pendingPromises.get(key) as R\n        }\n\n        // Create a new promise to prevent duplicate processing\n        const resultPromise = (async () => {\n          try {\n            // Ensure persistor is connected and ready\n            await ensurePersistorIsReady(persistor)\n\n            // Check cache — fall back to delegate if persistor is unavailable\n            let cached: R | null = null\n            try {\n              cached = deserialize<R>(await persistor.get(key))\n            } catch {\n              // persistor unavailable — fall through to delegate\n            }\n            if (cached !== null) {\n              return cached\n            }\n\n            // No cached value\n            const result = await delegate(...args)\n\n            // Calculate expiry\n            const expiry =\n              typeof options.expiry === 'function'\n                ? options.expiry(args, result)\n                : options.expiry\n\n            // Ensure persistor is still connected and ready\n            await ensurePersistorIsReady(persistor)\n\n            // Save to cache — best-effort, ignore errors\n            try {\n              const serialized = serialize(result)\n              const setOptions = toSetOptions(expiry)\n              await persistor.set(key, serialized, setOptions)\n            } catch {\n              // persistor unavailable — result still returned to caller\n            }\n\n            // Return result\n            return result\n          } finally {\n            pendingPromises.delete(key)\n          }\n        })()\n\n        // Store promise until it resolves or fails\n        pendingPromises.set(key, resultPromise)\n\n        return resultPromise\n      }\n    },\n  }\n  return cache\n}\n\nconst ensurePersistorIsReady = async (persistor: IPersistor) => {\n  // Persistor is connected and ready\n  /* istanbul ignore else */\n  if (persistor.isReady) {\n    return\n  }\n\n  // Persistor is connecting but not ready\n  /* istanbul ignore next */\n  if (persistor.isOpen) {\n    await new Promise<void>((resolve) => {\n      if (persistor.isReady) return resolve()\n      persistor.once('ready', resolve)\n      return\n    })\n  }\n\n  // Persistor should connect\n  /* istanbul ignore next */\n  try {\n    await persistor.connect()\n  } catch (err) {\n    /* istanbul ignore next */\n    if ((err as Error).message === 'Socket already opened') {\n      // Connection is already in progress\n      await new Promise<void>((resolve) => {\n        if (persistor.isReady) return resolve()\n        persistor.once('ready', resolve)\n      })\n    }\n  }\n}\n","import { add, sub } from 'date-fns'\n\nexport { add, sub }\n\nexport const SECOND = 1000\nexport const MINUTE = 60 * SECOND\nexport const HOUR = 60 * MINUTE\nexport const DAY = 24 * HOUR\nexport const WEEK = 7 * DAY\n\nexport const seconds = (num: number) => num * SECOND\nexport const minutes = (num: number) => num * MINUTE\nexport const hours = (num: number) => num * HOUR\nexport const days = (num: number) => num * DAY\nexport const weeks = (num: number) => num * WEEK\n\nexport const today = (hours = 0, minutes = 0, seconds = 0) => {\n  const local = new Date()\n  const utc = Date.UTC(\n    local.getUTCFullYear(),\n    local.getUTCMonth(),\n    local.getUTCDate(),\n    hours,\n    minutes,\n    seconds\n  )\n  return new Date(utc)\n}\n\nexport const tomorrow = (hours = 0, minutes = 0, seconds = 0) =>\n  add(today(hours, minutes, seconds), { days: 1 })\n"],"mappings":"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"}