"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CachedFileSystem = void 0; const path_1 = require("path"); const graceful_fs_1 = __importDefault(require("graceful-fs")); const async_sema_1 = require("async-sema"); const fsReadFile = graceful_fs_1.default.promises.readFile; const fsReadlink = graceful_fs_1.default.promises.readlink; const fsStat = graceful_fs_1.default.promises.stat; class CachedFileSystem { constructor({ cache, fileIOConcurrency, }) { this.fileIOQueue = new async_sema_1.Sema(fileIOConcurrency); this.fileCache = cache?.fileCache ?? new Map(); this.statCache = cache?.statCache ?? new Map(); this.symlinkCache = cache?.symlinkCache ?? new Map(); if (cache) { cache.fileCache = this.fileCache; cache.statCache = this.statCache; cache.symlinkCache = this.symlinkCache; } } async readlink(path) { const cached = this.symlinkCache.get(path); if (cached !== undefined) return cached; // This is not awaiting the response, so that the cache is instantly populated and // future calls serve the Promise from the cache const readlinkPromise = this.executeFileIO(path, this._internalReadlink); this.symlinkCache.set(path, readlinkPromise); return readlinkPromise; } async readFile(path) { const cached = this.fileCache.get(path); if (cached !== undefined) return cached; // This is not awaiting the response, so that the cache is instantly populated and // future calls serve the Promise from the cache const readFilePromise = this.executeFileIO(path, this._internalReadFile); this.fileCache.set(path, readFilePromise); return readFilePromise; } async stat(path) { const cached = this.statCache.get(path); if (cached !== undefined) return cached; // This is not awaiting the response, so that the cache is instantly populated and // future calls serve the Promise from the cache const statPromise = this.executeFileIO(path, this._internalStat); this.statCache.set(path, statPromise); return statPromise; } async _internalReadlink(path) { try { const link = await fsReadlink(path); // also copy stat cache to symlink const stats = this.statCache.get(path); if (stats) this.statCache.set((0, path_1.resolve)(path, link), stats); return link; } catch (e) { if (e.code !== 'EINVAL' && e.code !== 'ENOENT' && e.code !== 'UNKNOWN') throw e; return null; } } async _internalReadFile(path) { try { return (await fsReadFile(path)).toString(); } catch (e) { if (e.code === 'ENOENT' || e.code === 'EISDIR') { return null; } throw e; } } async _internalStat(path) { try { return await fsStat(path); } catch (e) { if (e.code === 'ENOENT') { return null; } throw e; } } async executeFileIO(path, fileIO) { await this.fileIOQueue.acquire(); try { return fileIO.call(this, path); } finally { this.fileIOQueue.release(); } } } exports.CachedFileSystem = CachedFileSystem;