import fsSyncOriginal from 'fs' import fsPromisesOriginal from 'fs/promises' import crypto from 'crypto' import path from 'path' import os from 'os' export default class FSCache { constructor(options = {}, fsSync, fsPromises) { this.fsSync = fsSync || fsSyncOriginal this.fsPromises = fsPromises || fsPromisesOriginal this.id = crypto.randomBytes(15).toString('base64').replace(/\//g, '-') this.parse_json = options.parse_json ?? true this.prefix = options.prefix ? options.prefix + '-' : '' this.hash_alg = options.hash_alg || 'md5' this.cache_dir = options.cache_dir || path.join(os.tmpdir(), this.id) // Verify hash algorithm is supported on this system crypto.createHash(this.hash_alg) this.fsSync.mkdirSync(this.cache_dir, { recursive: true }) } _parseCacheData(data, overwrite = {}) { return overwrite.parse_json ?? this.parse_json ? JSON.parse(data) : data } _parseSetData(data, overwrite = {}) { return overwrite.parse_json ?? this.parse_json ? JSON.stringify(data) : data } hash(name) { return crypto.hash(this.hash_alg, name) } get(name, fallback, opts) { return this.fsPromises.readFile( path.join(this.cache_dir, this.hash(name)), { encoding: 'utf8' } ) .then( data => this._parseCacheData(data, opts), err => (fallback) ) } getSync(name, fallback, opts) { let data; try { data = this.fsSync.readFileSync( path.join(this.cache_dir, this.hash(name)), { encoding: 'utf8' } ) } catch { return fallback } return this._parseCacheData(data, opts) } set(name, data, opts = {}) { try { return this.fsPromises.writeFile( path.join(this.cache_dir, this.hash(name)), this._parseSetData(data, opts), { encoding: opts.encoding || 'utf8' } ) } catch (err) { return Promise.reject(err) } } setSync(name, data, opts = {}) { this.fsSync.writeFileSync( path.join(this.cache_dir, this.hash(name)), this._parseSetData(data, opts), { encoding: opts.encoding || 'utf8' } ) } }