{"version":3,"file":"cache-Db3ibG1u.cjs","names":["SequentialInterval"],"sources":["../src/cache/CachedItem.ts","../src/cache/CacheNamespace.ts","../src/cache/CacheAdapter.ts","../src/cache/MemoryCache.ts"],"sourcesContent":["import type { Logger } from '@wcaservices/logger';\nimport { addSeconds } from 'date-fns';\nimport { formatTimestamp } from '../lib';\nimport { CacheAdapter } from './CacheAdapter';\n\n// @__NO_SIDE_EFFECTS__\nexport class CachedItem<T = unknown> {\n    protected static INFINITE_TTL_TIMESTAMP = new Date(2999, 0, 0).getTime();\n    protected static createExpiryDate(ttlSeconds: number): Date {\n        if (ttlSeconds > 0) {\n            return addSeconds(new Date(), ttlSeconds);\n        }\n        \n        if (ttlSeconds === 0 || ttlSeconds === -1) {\n            return new Date(this.INFINITE_TTL_TIMESTAMP);\n        }\n        \n        throw new Error(`Invalid cache TTL provided: ${ttlSeconds}`);\n    }\n    \n    public expiresAt: Date;\n    public readonly logger?: Logger;\n    \n    constructor(\n        protected readonly config: CachedItemConfig<T>,\n        protected readonly store: CacheAdapter,\n    ) {\n        this.expiresAt = CachedItem.createExpiryDate(config.ttlSeconds);\n        this.logger = store.logger?.child({\n            cache: {\n                item: this,\n            }\n        });\n    }\n    \n    public get value() {\n        return this.config.value;\n    };\n    \n    public isExpired() {\n        const isExpired = this.expiresAt.getTime() < Date.now();\n        \n        if (isExpired && this.logger) {\n            this.logger.debug(`Cache item \"${this.config.key}\" expired ${formatTimestamp(this.expiresAt, { format: 'relative' })}`);\n        }\n        \n        return isExpired;\n    }\n    \n    public delete() {\n        this.store.delete(this.config.key);\n    }\n    \n    public get key() {\n        return this.config.key;\n    }\n}\n\n\nexport interface CachedItemConfig<TValue = unknown> {\n    key: string;\n    value: TValue,\n    ttlSeconds: number,\n}","import type { CacheAdapter } from './CacheAdapter';\nimport { CachedItem, type CachedItemConfig } from './CachedItem';\n\nexport class CacheNamespace implements Partial<CacheAdapter> {\n    // @__NO_SIDE_EFFECTS__\n    constructor(\n        protected readonly namespace: string,\n        protected readonly cache: CacheAdapter,\n    ) {}\n    \n    /**\n     * Format the provided cache key to include this namespace's key prefix.\n     * @param key\n     */\n    protected formatKey(key: string) {\n        return `${this.namespace}:${key}`;\n    }\n    \n    /**\n     * Retrieve an item from the cache by its key.\n     * @param key\n     */\n    public get<TType>(key: string): CachedItem<TType> | null {\n        return this.cache.get(this.formatKey(key));\n    }\n    \n    /**\n     * Remove the provided key from the cache (if it exists)\n     * @param key\n     */\n    public delete(key: string): void {\n        this.cache.delete(this.formatKey(key));\n    }\n    \n    /**\n     * Write (or overwrite) the provided value to the cache.\n     * @param config\n     */\n    public set<TValue>(config: CachedItemConfig<TValue>): TValue {\n        return this.cache.set({\n            ...config,\n            key: this.formatKey(config.key),\n        });\n    }\n    \n    /**\n     * Cache the result of the provided callback.\n     * The callback will be executed only when the provided cache key is unset or has expired.\n     * @param item\n     */\n    public remember<TValue>(item: {\n        key: string;\n        ttlSeconds: number;\n        handler: () => TValue;\n    }): TValue {\n        return this.cache.remember({\n            ...item,\n            key: this.formatKey(item.key),\n        });\n    }\n    \n    /**\n     * Clear out expired entries from the internal cache store.\n     */\n    public prune() {\n        return this.cache.prune();\n    }\n}","import type { Logger } from '@wcaservices/logger';\nimport { secondsToMilliseconds } from 'date-fns';\nimport { formatTimestamp, SequentialInterval } from '../lib';\nimport { CachedItem, type CachedItemConfig } from './CachedItem';\nimport { CacheNamespace } from './CacheNamespace';\n\nexport class CacheAdapter {\n    public readonly logger?: Logger;\n    protected readonly _store: CacheStore;\n    protected readonly pruneInterval: SequentialInterval;\n    \n    constructor(config: CacheAdapterConfig) {\n        this._store = config.store;\n        this.logger = config.logger;\n        this.pruneInterval = new SequentialInterval({\n            callback: () => this.prune(),\n            intervalMs: config.pruneIntervalMs || secondsToMilliseconds(60),\n            startImmediately: config.pruneIntervalMs !== 0,\n        });\n    }\n    \n    /**\n     * Proxy of the current cache but where all keys are namespaced to the provided string to avoid naming conflicts.\n     * @param namespace Identifier to use as the prefix for cache keys.\n     */\n    public createNamespace(namespace: string): CacheNamespace {\n        return new CacheNamespace(namespace, this);\n    }\n    \n    /**\n     * Retrieve an item from the cache by its key.\n     * @param key\n     */\n    public get<TType>(key: string): CachedItem<TType> | null {\n        const item = this._store.get(key);\n        if (!item) {\n            return null;\n        }\n        if (item.isExpired()) {\n            this.delete(key);\n            item.logger?.debug(`Removed expired '${key}' from cache.`)\n            return null;\n        }\n        return item as CachedItem<TType>;\n    }\n    \n    /**\n     * Remove the provided key from the cache (if it exists)\n     * @param key\n     */\n    public delete(key: string): void {\n        const logger = this.logger?.child({ cache: { key } });\n        \n        if (!this._store.has(key)) {\n            logger?.warn(`Cache key '${key}' does not exist`);\n            return;\n        }\n        \n        this._store.delete(key);\n        this.logger?.debug(`Removed '${key}' from cache`);\n    }\n    \n    /**\n     * Write (or overwrite) the provided value to the cache.\n     * @param config\n     */\n    public set<TValue>(config: CachedItemConfig<TValue>): TValue {\n        const existingItem = this.get(config.key);\n        const item = new CachedItem(config, this);\n        this._store.set(item.key, item);\n        \n        if (existingItem) {\n            existingItem.logger?.debug(`Overwrote existing '${existingItem.key}' entry`);\n        } else {\n            item.logger?.debug(`Added '${item.key}' to the cache`);\n        }\n        \n        \n        return item.value;\n    }\n    \n    /**\n     * Cache the result of the provided callback.\n     * The callback will be executed only when the provided cache key is unset or has expired.\n     * @param item\n     */\n    public remember<TValue>(item: {\n        key: string;\n        ttlSeconds: number;\n        handler: () => TValue;\n    }): TValue {\n        const cachedItem = this.get(item.key);\n        \n        if (cachedItem) {\n            cachedItem.logger?.debug(`Using cached value for '${item.key}' remember() cache. Next run: ${formatTimestamp(cachedItem.expiresAt, { format: 'relative' })}`);\n            return cachedItem.value as TValue;\n        }\n        \n        const value = item.handler();\n        this.logger?.debug(`Fired handler hook for '${item.key}' remember() cache...`);\n        this.set({\n            key: item.key,\n            value,\n            ttlSeconds: item.ttlSeconds,\n        });\n        \n        return value;\n    }\n    \n    /**\n     * Clear out expired entries from the internal cache store.\n     */\n    public prune(): void {\n        this.logger?.debug(`Validating remaining TTL for ${this._store.size} items...`);\n        let activeCount = 0;\n        let expiredCount = 0;\n        \n        for (const [key, item] of this._store.entries()) {\n            if (!item.isExpired()) {\n                activeCount++;\n                continue;\n            }\n            expiredCount++;\n            this.delete(key);\n        }\n        \n        this.logger?.debug(`Prune completed. ${activeCount} items are still active. ${expiredCount} were removed.`);\n    }\n    \n    /**\n     * Remove all items from the local cache store.\n     */\n    public deleteAll(): void {\n        this._store.clear();\n    }\n}\n\ninterface CacheAdapterConfig {\n    store: CacheStore;\n    logger?: Logger;\n    \n    /**\n     * How often should the cache be checked for expired entries.\n     */\n    pruneIntervalMs?: number;\n}\n\ninterface CacheStore {\n    get(key: string): CachedItem | null | undefined;\n    set(key: string, value: CachedItem): any;\n    delete(key: string): void;\n    entries(): MapIterator<[string, CachedItem]>;\n    has(key: string): boolean;\n    clear(): void;\n    size: number;\n}\n\n/**\n * Todo: Add CacheAdapterAsync that implements caching using these asynchronous methods.\n */\ninterface CacheStoreAsync {\n    get(key: string): Promise<CachedItem | null | undefined>;\n    set(key: string, value: CachedItem): Promise<any>;\n    delete(key: string): Promise<void>;\n    entries(): Promise<MapIterator<[string, CachedItem]>>;\n}","import { createLogger } from '@wcaservices/logger';\nimport { CacheAdapter } from './CacheAdapter';\nimport type { CachedItem } from './CachedItem';\n\n// @__NO_SIDE_EFFECTS__\nexport const MemoryCache = new CacheAdapter({\n    store: new Map<string, CachedItem>(),\n    logger: createLogger('MemoryCache')\n});"],"mappings":"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"}