{"version":3,"file":"BaseCacheEngine.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/BaseCacheEngine.ts"],"sourcesContent":["import { getCacheConfig } from \"../config\";\r\nimport { CacheDriverInterface } from \"../types\";\r\nimport isForbiddenKey from \"./isForbiddenKey\";\r\n\r\n/**\r\n * Base engine for the synchronous storage backends (Web Storage and the\r\n * in-memory runtime store).\r\n *\r\n * The backends themselves answer synchronously; every public method here\r\n * lifts their result into a promise (`Promise.resolve(...)`, through the\r\n * `read` / `write` / `delete` helpers) so that all drivers — including\r\n * the IndexedDB one, which cannot be synchronous — expose the exact same\r\n * async contract. No behavior changes for the Web Storage drivers: the\r\n * work still happens in the same tick, only the return type differs.\r\n */\r\nexport default class BaseCacheEngine implements CacheDriverInterface {\r\n  /**\r\n   * Cache storage engine\r\n   */\r\n  public storage: any;\r\n\r\n  /**\r\n   * Prefix key\r\n   */\r\n  public prefixKey: string = \"\";\r\n\r\n  /**\r\n   * Value parser\r\n   */\r\n  protected _valueParser = this.parseValue.bind(this);\r\n\r\n  /**\r\n   * Value converter\r\n   */\r\n  protected _valueConverter = this.convertValue.bind(this);\r\n\r\n  /**\r\n   * set value parser\r\n   */\r\n  public setValueParser(parser: any) {\r\n    this._valueParser = parser;\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Set value converter\r\n   */\r\n  public setValueConverter(converter: any) {\r\n    this._valueConverter = converter;\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Read a raw entry from the underlying (synchronous) storage\r\n   */\r\n  protected read(storageKey: string): Promise<any> {\r\n    return Promise.resolve(this.storage.getItem(storageKey));\r\n  }\r\n\r\n  /**\r\n   * Write a raw entry into the underlying (synchronous) storage\r\n   */\r\n  protected write(storageKey: string, value: any): Promise<void> {\r\n    return Promise.resolve(this.storage.setItem(storageKey, value));\r\n  }\r\n\r\n  /**\r\n   * Delete a raw entry from the underlying (synchronous) storage\r\n   */\r\n  protected delete(storageKey: string): Promise<void> {\r\n    return Promise.resolve(this.storage.removeItem(storageKey));\r\n  }\r\n\r\n  /**\r\n   * Get vale from storage engine\r\n   */\r\n  public async get(key: string, defaultValue?: any) {\r\n    const value = await this.read(this.getKey(key));\r\n\r\n    if (value === null || value === undefined) return defaultValue;\r\n\r\n    try {\r\n      const cachedData = this._valueParser(value);\r\n\r\n      // check if there is a cache timestamp\r\n      // if it is lower than current timestamp\r\n      // then remove the key from storage\r\n      if (cachedData.expiresAt && cachedData.expiresAt < new Date().getTime()) {\r\n        await this.remove(key);\r\n        return defaultValue;\r\n      }\r\n\r\n      return cachedData.data;\r\n    } catch (error) {\r\n      await this.remove(key);\r\n      return defaultValue;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Set data into storage engine\r\n   */\r\n  public async set(key: string, value: any, expiresAfter?: number) {\r\n    const expireTime: number | false =\r\n      expiresAfter !== undefined\r\n        ? expiresAfter\r\n        : ((getCacheConfig(\"expiresAfter\") || 0) as number);\r\n\r\n    const expiresAt = expireTime\r\n      ? new Date().getTime() + expireTime * 1000\r\n      : undefined;\r\n\r\n    await this.write(\r\n      this.getKey(key),\r\n      this._valueConverter({\r\n        data: value,\r\n        expiresAt,\r\n      })\r\n    );\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Parse stored value\r\n   */\r\n  protected parseValue(value: any) {\r\n    try {\r\n      return JSON.parse(value);\r\n    } catch (error) {\r\n      return value;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Set the mechanism to store data\r\n   */\r\n  protected convertValue(value: any) {\r\n    return JSON.stringify(value);\r\n  }\r\n\r\n  /**\r\n   * Determine whether the cache engine has a live entry for the key\r\n   *\r\n   * An expired entry counts as absent — and is evicted on the spot —\r\n   * so `has()` and `get()` agree on every plain engine. Before 2.0.0 the\r\n   * Web Storage engines reported an expired entry as present while the\r\n   * runtime driver reported it as absent; consumers that branched on\r\n   * `has()` then read `get()` saw a value appear and vanish.\r\n   *\r\n   * The encrypted engines override this: their payload is opaque until\r\n   * decrypted, so they answer on presence alone.\r\n   */\r\n  public async has(key: string): Promise<boolean> {\r\n    const value = await this.read(this.getKey(key));\r\n\r\n    if (value === null || value === undefined) return false;\r\n\r\n    try {\r\n      const cachedData = this._valueParser(value);\r\n\r\n      if (cachedData.expiresAt && cachedData.expiresAt < new Date().getTime()) {\r\n        await this.remove(key);\r\n        return false;\r\n      }\r\n    } catch (error) {\r\n      // Unreadable but present: report it as present and let `get()`\r\n      // do the self-healing eviction on the next read.\r\n      return true;\r\n    }\r\n\r\n    return true;\r\n  }\r\n\r\n  /**\r\n   * Remove key from storage\r\n   */\r\n  public async remove(key: string) {\r\n    await this.delete(this.getKey(key));\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Get a proper key\r\n   */\r\n  public getKey(key: string): string {\r\n    key = (this.getPrefixKey() || \"\") + key;\r\n\r\n    return key;\r\n  }\r\n\r\n  /**\r\n   * Get prefix key\r\n   */\r\n  public getPrefixKey(): string {\r\n    return this.prefixKey;\r\n  }\r\n\r\n  /**\r\n   * Set prefix key\r\n   */\r\n  public setPrefixKey(key: string) {\r\n    this.prefixKey = key;\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * List the keys currently held by the storage engine\r\n   *\r\n   * The returned array is a snapshot: Web Storage re-indexes itself on\r\n   * every removal, so iterating `storage.length` while removing entries\r\n   * silently skips keys.\r\n   */\r\n  protected async storageKeys(): Promise<string[]> {\r\n    const storage = this.storage;\r\n\r\n    if (!storage) return [];\r\n\r\n    // Web Storage exposes an ordered `length` + `key(index)` pair.\r\n    if (\r\n      typeof storage.key === \"function\" &&\r\n      typeof storage.length === \"number\"\r\n    ) {\r\n      const keys: string[] = [];\r\n\r\n      for (let index = 0; index < storage.length; index++) {\r\n        const key = storage.key(index);\r\n\r\n        if (key !== null) keys.push(key);\r\n      }\r\n\r\n      return keys;\r\n    }\r\n\r\n    return Object.keys(storage);\r\n  }\r\n\r\n  /**\r\n   * List the caller-facing keys owned by this engine\r\n   *\r\n   * With a prefix configured, only the owned keys are listed and the\r\n   * prefix is stripped so the result can be passed straight back to\r\n   * `get()` / `remove()`. Without one the engine owns the whole\r\n   * namespace, so everything in storage is returned.\r\n   */\r\n  public async keys(): Promise<string[]> {\r\n    const prefix = this.getPrefixKey();\r\n    const keys = await this.storageKeys();\r\n\r\n    if (!prefix) return keys;\r\n\r\n    return keys\r\n      .filter(key => key.startsWith(prefix))\r\n      .map(key => key.slice(prefix.length));\r\n  }\r\n\r\n  /**\r\n   * Read every live entry owned by this engine\r\n   *\r\n   * Built on top of `keys()` / `get()`, so it costs one read per owned\r\n   * key. The returned object has a null prototype and every key is\r\n   * defined as an own property, so a stored key of `__proto__`,\r\n   * `constructor` or `prototype` lands as inert data instead of reaching\r\n   * a prototype setter.\r\n   */\r\n  public async getAll(): Promise<Record<string, any>> {\r\n    const entries: Record<string, any> = Object.create(null);\r\n    const missing = Symbol(\"missing\");\r\n\r\n    for (const key of await this.keys()) {\r\n      const value = await this.get(key, missing);\r\n\r\n      if (value === missing) continue;\r\n\r\n      if (isForbiddenKey(key)) {\r\n        Object.defineProperty(entries, key, {\r\n          value,\r\n          writable: true,\r\n          enumerable: true,\r\n          configurable: true,\r\n        });\r\n\r\n        continue;\r\n      }\r\n\r\n      entries[key] = value;\r\n    }\r\n\r\n    return entries;\r\n  }\r\n\r\n  /**\r\n   * Clear the cache storage\r\n   *\r\n   * Only the keys owned by this engine's prefix are removed, so several\r\n   * apps sharing the same origin (and therefore the same localStorage /\r\n   * sessionStorage) can clear their own namespace without destroying\r\n   * each other's data.\r\n   *\r\n   * When no prefix is configured the engine owns the whole namespace,\r\n   * so the historical behavior is kept and the entire storage is wiped.\r\n   */\r\n  public async clear() {\r\n    const prefix = this.getPrefixKey();\r\n\r\n    if (!prefix) {\r\n      await Promise.resolve(this.storage.clear());\r\n\r\n      return this;\r\n    }\r\n\r\n    for (const key of await this.storageKeys()) {\r\n      if (key.startsWith(prefix)) {\r\n        await this.delete(key);\r\n      }\r\n    }\r\n\r\n    return this;\r\n  }\r\n}\r\n"],"mappings":"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"}