tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PersistentCache.sys.mjs (2547B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /**
      6 * A file (disk) based persistent cache of a JSON serializable object.
      7 */
      8 export class PersistentCache {
      9  /**
     10   * Create a cache object based on a name.
     11   *
     12   * @param {string} name Name of the cache. It will be used to create the filename.
     13   * @param {boolean} preload (optional). Whether the cache should be preloaded from file. Defaults to false.
     14   */
     15  constructor(name, preload = false) {
     16    this.name = name;
     17    this._filename = `activity-stream.${name}.json`;
     18    if (preload) {
     19      this._load();
     20    }
     21  }
     22 
     23  /**
     24   * Set a value to be cached with the specified key.
     25   *
     26   * @param {string} key The cache key.
     27   * @param {object} value The data to be cached.
     28   */
     29  async set(key, value) {
     30    const data = await this._load();
     31    data[key] = value;
     32    await this._persist(data);
     33  }
     34 
     35  /**
     36   * Get a value from the cache.
     37   *
     38   * @param {string} key (optional) The cache key. If not provided, we return the full cache.
     39   * @returns {object} The cached data.
     40   */
     41  async get(key) {
     42    const data = await this._load();
     43    return key ? data[key] : data;
     44  }
     45 
     46  /**
     47   * Load the cache into memory if it isn't already.
     48   */
     49  _load() {
     50    return (
     51      this._cache ||
     52      // eslint-disable-next-line no-async-promise-executor
     53      (this._cache = new Promise(async (resolve, reject) => {
     54        let filepath;
     55        try {
     56          filepath = PathUtils.join(PathUtils.localProfileDir, this._filename);
     57        } catch (error) {
     58          reject(error);
     59          return;
     60        }
     61 
     62        let data = {};
     63        try {
     64          data = await IOUtils.readJSON(filepath);
     65        } catch (error) {
     66          if (
     67            // isInstance() is not available in node unit test. It should be safe to use instanceof as it's directly from IOUtils.
     68            // eslint-disable-next-line mozilla/use-isInstance
     69            !(error instanceof DOMException) ||
     70            error.name !== "NotFoundError"
     71          ) {
     72            console.error(`Failed to parse ${this._filename}:`, error.message);
     73          }
     74        }
     75 
     76        resolve(data);
     77      }))
     78    );
     79  }
     80 
     81  /**
     82   * Persist the cache to file.
     83   */
     84  async _persist(data) {
     85    const filepath = PathUtils.join(PathUtils.localProfileDir, this._filename);
     86    await IOUtils.writeJSON(filepath, data, {
     87      tmpPath: `${filepath}.tmp`,
     88    });
     89  }
     90 }