mastercache
Version:
Multi-tier cache module for Node.js. Redis, Upstash, CloudfareKV, File, in-memory and others drivers
1 lines • 11.6 kB
Source Map (JSON)
{"version":3,"sources":["../../../../src/drivers/database/database.ts","../../../../../../node_modules/.pnpm/@lukeed+ms@2.0.2/node_modules/@lukeed/ms/dist/index.mjs","../../../../src/helpers.ts","../../../../src/drivers/base-driver.ts"],"sourcesContent":["import { resolveTtl } from '../../helpers';\nimport { BaseDriver } from '../base-driver';\nimport type { DatabaseConfig, CacheDriver, DatabaseAdapter } from '../../types/main';\n\n/**\n * A store that use a database to store cache entries\n *\n * You should provide an adapter that will handle the database interactions\n */\nexport class DatabaseDriver extends BaseDriver implements CacheDriver<true> {\n type = 'l2' as const;\n\n /**\n * The underlying adapter\n */\n #adapter: DatabaseAdapter;\n\n /**\n * A promise that resolves when the table is created\n */\n #initialized: Promise<void>;\n\n /**\n * Pruning interval\n */\n #pruneInterval?: NodeJS.Timeout;\n\n constructor(adapter: DatabaseAdapter, config: DatabaseConfig, isNamespace = false) {\n super(config);\n this.#adapter = adapter;\n\n if (isNamespace) {\n this.#initialized = Promise.resolve();\n return;\n }\n\n this.#adapter.setTableName(config.tableName || 'mastercache');\n\n if (config.autoCreateTable !== false) {\n this.#initialized = this.#adapter.createTableIfNotExists();\n } else {\n this.#initialized = Promise.resolve();\n }\n\n if (config.pruneInterval === false) return;\n this.#startPruneInterval(resolveTtl(config.pruneInterval)!);\n }\n\n /**\n * Start the interval that will prune expired entries\n * Maybe rework this using a node Worker ?\n */\n #startPruneInterval(interval: number) {\n this.#pruneInterval = setInterval(async () => {\n await this.#initialized;\n await this.#adapter\n .pruneExpiredEntries()\n .catch((err) => console.error('[mastercache] failed to prune expired entries', err));\n }, interval);\n }\n\n /**\n * Check if the given timestamp is expired\n */\n #isExpired(expiration: number | null) {\n return expiration !== null && expiration < Date.now();\n }\n\n /**\n * Returns a new instance of the driver namespaced\n */\n namespace(namespace: string) {\n const store = new (this.constructor as any)(\n this.#adapter,\n { ...this.config, prefix: this.createNamespacePrefix(namespace) },\n true,\n );\n\n return store;\n }\n\n /**\n * Get a value from the cache\n */\n async get(key: string) {\n await this.#initialized;\n\n const result = await this.#adapter.get(this.getItemKey(key));\n if (!result) return;\n\n if (this.#isExpired(result.expiresAt)) {\n await this.#adapter.delete(key);\n return;\n }\n\n return result.value;\n }\n\n /**\n * Get the value of a key and delete it\n *\n * Returns the value if the key exists, undefined otherwise\n */\n async pull(key: string): Promise<string | undefined> {\n const value = await this.get(key);\n if (value) await this.delete(key);\n\n return value;\n }\n\n /**\n * Set a value in the cache\n * Returns true if the value was set, false otherwise\n */\n async set(key: string, value: any, ttl?: number) {\n await this.#initialized;\n await this.#adapter.set({\n key: this.getItemKey(key),\n value,\n expiresAt: ttl ? new Date(Date.now() + ttl) : null,\n });\n\n return true;\n }\n\n /**\n * Check if a key exists in the cache\n */\n async has(key: string) {\n await this.#initialized;\n const result = await this.get(key);\n\n if (!result) return false;\n return true;\n }\n\n /**\n * Remove all items from the cache\n */\n async clear() {\n await this.#initialized;\n\n await this.#adapter.clear(this.prefix);\n }\n\n /**\n * Delete a key from the cache\n * Returns true if the key was deleted, false otherwise\n */\n async delete(key: string) {\n await this.#initialized;\n return this.#adapter.delete(this.getItemKey(key));\n }\n\n /**\n * Delete multiple keys from the cache\n */\n async deleteMany(keys: string[]) {\n await this.#initialized;\n\n keys = keys.map((key) => this.getItemKey(key));\n const result = await this.#adapter.deleteMany(keys);\n\n return result > 0;\n }\n\n /**\n * Disconnect from the database\n */\n async disconnect() {\n if (this.#pruneInterval) {\n clearInterval(this.#pruneInterval);\n }\n\n await this.#adapter.disconnect();\n }\n}\n","var RGX = /^(-?(?:\\d+)?\\.?\\d+) *(m(?:illiseconds?|s(?:ecs?)?))?(s(?:ec(?:onds?|s)?)?)?(m(?:in(?:utes?|s)?)?)?(h(?:ours?|rs?)?)?(d(?:ays?)?)?(w(?:eeks?|ks?)?)?(y(?:ears?|rs?)?)?$/,\n\tSEC = 1e3,\n\tMIN = SEC * 60,\n\tHOUR = MIN * 60,\n\tDAY = HOUR * 24,\n\tYEAR = DAY * 365.25;\n\nexport function parse(val) {\n\tvar num, arr = val.toLowerCase().match(RGX);\n\tif (arr != null && (num = parseFloat(arr[1]))) {\n\t\tif (arr[3] != null) return num * SEC;\n\t\tif (arr[4] != null) return num * MIN;\n\t\tif (arr[5] != null) return num * HOUR;\n\t\tif (arr[6] != null) return num * DAY;\n\t\tif (arr[7] != null) return num * DAY * 7;\n\t\tif (arr[8] != null) return num * YEAR;\n\t\treturn num;\n\t}\n}\n\nfunction fmt(val, pfx, str, long) {\n\tvar num = (val | 0) === val ? val : ~~(val + 0.5);\n\treturn pfx + num + (long ? (' ' + str + (num != 1 ? 's' : '')) : str[0]);\n}\n\nexport function format(num, long) {\n\tvar pfx = num < 0 ? '-' : '', abs = num < 0 ? -num : num;\n\tif (abs < SEC) return num + (long ? ' ms' : 'ms');\n\tif (abs < MIN) return fmt(abs / SEC, pfx, 'second', long);\n\tif (abs < HOUR) return fmt(abs / MIN, pfx, 'minute', long);\n\tif (abs < DAY) return fmt(abs / HOUR, pfx, 'hour', long);\n\tif (abs < YEAR) return fmt(abs / DAY, pfx, 'day', long);\n\treturn fmt(abs / YEAR, pfx, 'year', long);\n}\n","import { parse } from '@lukeed/ms';\n\nimport type { Duration } from './types/main';\n\n/**\n * Resolve a TTL value to a number in milliseconds\n */\nexport function resolveTtl(ttl?: Duration, defaultTtl: Duration = 30_000) {\n if (typeof ttl === 'number') return ttl;\n\n /**\n * If the TTL is null, it means the value should never expire\n */\n if (ttl === null) {\n return undefined;\n }\n\n if (ttl === undefined) {\n if (typeof defaultTtl === 'number') return defaultTtl;\n if (typeof defaultTtl === 'string') return parse(defaultTtl);\n\n return undefined;\n }\n\n return parse(ttl);\n}\n\n/**\n * Useful for creating a return value that can be destructured\n * or iterated over.\n *\n * See : https://antfu.me/posts/destructuring-with-object-or-array\n */\nexport function createIsomorphicDestructurable<\n T extends Record<string, unknown>,\n A extends readonly any[],\n>(obj: T, arr: A): T & A {\n const clone = { ...obj };\n\n Object.defineProperty(clone, Symbol.iterator, {\n enumerable: false,\n value() {\n let index = 0;\n return {\n next: () => ({\n value: arr[index++],\n done: index > arr.length,\n }),\n };\n },\n });\n\n return clone as T & A;\n}\n","import type { DriverCommonOptions } from '../types/main';\n\nexport abstract class BaseDriver {\n /**\n * Current cache prefix\n */\n protected prefix: string;\n\n constructor(protected config: DriverCommonOptions) {\n this.prefix = this.#sanitizePrefix(config.prefix);\n }\n\n /**\n * Sanitizes the cache prefix by removing any trailing colons\n */\n #sanitizePrefix(prefix?: string) {\n if (!prefix) return '';\n return prefix.replace(/:+$/, '');\n }\n\n /**\n * Creates a namespace prefix by concatenating the cache prefix with the given namespace\n * If the cache prefix is not defined, the namespace is returned as is\n */\n protected createNamespacePrefix(namespace: string) {\n const sanitizedPrefix = this.#sanitizePrefix(this.prefix);\n return sanitizedPrefix ? `${sanitizedPrefix}:${namespace}` : namespace;\n }\n\n /**\n * Returns the cache key with the prefix added to it, if a prefix is defined\n */\n protected getItemKey(key: string) {\n return this.prefix ? `${this.prefix}:${key}` : key;\n }\n}\n"],"mappings":"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","names":[]}