{"version":3,"file":"EncryptedLocalStorageDriver.mjs","names":[],"sources":["../../../../../../../cache/src/drivers/EncryptedLocalStorageDriver.ts"],"sourcesContent":["import { getCacheConfig } from \"../config\";\r\nimport { CacheDriverInterface } from \"../types\";\r\nimport PlainLocalStorageDriver from \"./PlainLocalStorageDriver\";\r\n\r\nexport default class EncryptedLocalStorageDriver\r\n  extends PlainLocalStorageDriver\r\n  implements CacheDriverInterface\r\n{\r\n  /**\r\n   * Set data into storage engine\r\n   *\r\n   * Wraps the value in a `{data, expiresAt}` envelope (matching the\r\n   * plain driver shape) BEFORE encrypting, so the encrypted variant\r\n   * honors `expiresAfter` just like the plain drivers do.\r\n   *\r\n   * The configured `encrypt` is awaited: @mongez/encryption 2.x returns\r\n   * a promise (WebCrypto AES-GCM cannot be synchronous), while 1.x\r\n   * returned a string. `await` accepts both.\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    const encryption = getCacheConfig(\"encryption\");\r\n\r\n    const cypher = await encryption?.encrypt({\r\n      data: value,\r\n      expiresAt,\r\n    });\r\n\r\n    await this.write(this.getKey(key), cypher);\r\n\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Get value from storage engine\r\n   *\r\n   * Decrypts then unwraps the `{data, expiresAt}` envelope and checks\r\n   * expiry. For backward compatibility with legacy cyphers that were\r\n   * written before the envelope was introduced (no `data` / `expiresAt`\r\n   * keys), the decrypted value is returned as-is with no expiration.\r\n   *\r\n   * A cypher that fails to decrypt (tampered entry, rotated key,\r\n   * truncated write) must not make every subsequent read throw, so the\r\n   * poisoned entry is evicted and the default value is returned —\r\n   * the same self-healing behavior `BaseCacheEngine.get()` implements.\r\n   * With an authenticated cipher (AES-GCM) that eviction is also the\r\n   * tamper response: a modified cypher fails the tag check, so it is\r\n   * dropped rather than returned as data.\r\n   */\r\n  public async get(key: string, defaultValue: any = null) {\r\n    const value = await this.read(this.getKey(key));\r\n\r\n    if (!value) return defaultValue;\r\n\r\n    try {\r\n      const decrypted = await getCacheConfig(\"encryption\")?.decrypt(value);\r\n\r\n      // Legacy format detection: pre-envelope cyphers decrypt to\r\n      // arbitrary user data (string / number / object without\r\n      // `data` + `expiresAt` keys). Treat those as immortal entries.\r\n      if (\r\n        decrypted === null ||\r\n        decrypted === undefined ||\r\n        typeof decrypted !== \"object\" ||\r\n        !(\"data\" in decrypted)\r\n      ) {\r\n        return decrypted === null || decrypted === undefined\r\n          ? defaultValue\r\n          : decrypted;\r\n      }\r\n\r\n      if (decrypted.expiresAt && decrypted.expiresAt < new Date().getTime()) {\r\n        await this.remove(key);\r\n        return defaultValue;\r\n      }\r\n\r\n      return decrypted.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   * Determine whether the cache engine has a live entry for the key\r\n   *\r\n   * Presence is decided without decrypting: the cypher is opaque, and\r\n   * running the (now async) decrypt on every `has()` would leak both\r\n   * CPU and, on a rotated key, entries — an eviction is a write, and a\r\n   * read-shaped call should not silently rewrite storage. Expiry is\r\n   * therefore enforced by `get()`, which has to decrypt anyway.\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    return value !== null && value !== undefined;\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\r\n    return this;\r\n  }\r\n}\r\n"],"mappings":";;;;AAIA,IAAqB,8BAArB,cACU,wBAEV;;;;;;;;;;;;CAYE,MAAa,IAAI,KAAa,OAAY,cAAuB;EAC/D,MAAM,aACJ,iBAAiB,SACb,eACE,eAAe,cAAc,KAAK;EAE1C,MAAM,YAAY,8BACd,IAAI,KAAK,EAAC,CAAC,QAAQ,IAAI,aAAa,MACpC;EAIJ,MAAM,SAAS,MAFI,eAAe,YAEJ,CAAC,EAAE,QAAQ;GACvC,MAAM;GACN;EACF,CAAC;EAED,MAAM,KAAK,MAAM,KAAK,OAAO,GAAG,GAAG,MAAM;EAEzC,OAAO;CACT;;;;;;;;;;;;;;;;;CAkBA,MAAa,IAAI,KAAa,eAAoB,MAAM;EACtD,MAAM,QAAQ,MAAM,KAAK,KAAK,KAAK,OAAO,GAAG,CAAC;EAE9C,IAAI,CAAC,OAAO,OAAO;EAEnB,IAAI;GACF,MAAM,YAAY,MAAM,eAAe,YAAY,CAAC,EAAE,QAAQ,KAAK;GAKnE,IACE,cAAc,QACd,cAAc,UACd,OAAO,cAAc,YACrB,EAAE,UAAU,YAEZ,OAAO,cAAc,QAAQ,cAAc,SACvC,eACA;GAGN,IAAI,UAAU,aAAa,UAAU,6BAAY,IAAI,KAAK,EAAC,CAAC,QAAQ,GAAG;IACrE,MAAM,KAAK,OAAO,GAAG;IACrB,OAAO;GACT;GAEA,OAAO,UAAU;EACnB,SAAS,OAAO;GACd,MAAM,KAAK,OAAO,GAAG;GACrB,OAAO;EACT;CACF;;;;;;;;;;CAWA,MAAa,IAAI,KAA+B;EAC9C,MAAM,QAAQ,MAAM,KAAK,KAAK,KAAK,OAAO,GAAG,CAAC;EAE9C,OAAO,UAAU,QAAQ,UAAU;CACrC;;;;CAKA,MAAa,OAAO,KAAa;EAC/B,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,CAAC;EAElC,OAAO;CACT;AACF"}