| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919 | "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.ZipOpenFS = exports.getArchivePart = void 0;const tslib_1 = require("tslib");const fs_1 = require("fs");const FakeFS_1 = require("./FakeFS");const NodeFS_1 = require("./NodeFS");const ZipFS_1 = require("./ZipFS");const watchFile_1 = require("./algorithms/watchFile");const errors = tslib_1.__importStar(require("./errors"));const path_1 = require("./path");// Only file descriptors prefixed by those values will be forwarded to the ZipFS// instances. Note that the highest ZIP_MAGIC bit MUST NOT be set, otherwise the// resulting fd becomes a negative integer, which isn't supposed to happen per// the unix rules (caused problems w/ Go).//// Those values must be synced with packages/yarnpkg-pnp/sources/esm-loader/fspatch.ts//const ZIP_MASK = 0xff000000;const ZIP_MAGIC = 0x2a000000;/** * Extracts the archive part (ending in the first instance of `extension`) from a path. * * The indexOf-based implementation is ~3.7x faster than a RegExp-based implementation. */const getArchivePart = (path, extension) => {    let idx = path.indexOf(extension);    if (idx <= 0)        return null;    let nextCharIdx = idx;    while (idx >= 0) {        nextCharIdx = idx + extension.length;        if (path[nextCharIdx] === path_1.ppath.sep)            break;        // Disallow files named ".zip"        if (path[idx - 1] === path_1.ppath.sep)            return null;        idx = path.indexOf(extension, nextCharIdx);    }    // The path either has to end in ".zip" or contain an archive subpath (".zip/...")    if (path.length > nextCharIdx && path[nextCharIdx] !== path_1.ppath.sep)        return null;    return path.slice(0, nextCharIdx);};exports.getArchivePart = getArchivePart;class ZipOpenFS extends FakeFS_1.BasePortableFakeFS {    static async openPromise(fn, opts) {        const zipOpenFs = new ZipOpenFS(opts);        try {            return await fn(zipOpenFs);        }        finally {            zipOpenFs.saveAndClose();        }    }    get libzip() {        if (typeof this.libzipInstance === `undefined`)            this.libzipInstance = this.libzipFactory();        return this.libzipInstance;    }    constructor({ libzip, baseFs = new NodeFS_1.NodeFS(), filter = null, maxOpenFiles = Infinity, readOnlyArchives = false, useCache = true, maxAge = 5000, fileExtensions = null }) {        super();        this.fdMap = new Map();        this.nextFd = 3;        this.isZip = new Set();        this.notZip = new Set();        this.realPaths = new Map();        this.limitOpenFilesTimeout = null;        this.libzipFactory = typeof libzip !== `function`            ? () => libzip            : libzip;        this.baseFs = baseFs;        this.zipInstances = useCache ? new Map() : null;        this.filter = filter;        this.maxOpenFiles = maxOpenFiles;        this.readOnlyArchives = readOnlyArchives;        this.maxAge = maxAge;        this.fileExtensions = fileExtensions;    }    getExtractHint(hints) {        return this.baseFs.getExtractHint(hints);    }    getRealPath() {        return this.baseFs.getRealPath();    }    saveAndClose() {        (0, watchFile_1.unwatchAllFiles)(this);        if (this.zipInstances) {            for (const [path, { zipFs }] of this.zipInstances.entries()) {                zipFs.saveAndClose();                this.zipInstances.delete(path);            }        }    }    discardAndClose() {        (0, watchFile_1.unwatchAllFiles)(this);        if (this.zipInstances) {            for (const [path, { zipFs }] of this.zipInstances.entries()) {                zipFs.discardAndClose();                this.zipInstances.delete(path);            }        }    }    resolve(p) {        return this.baseFs.resolve(p);    }    remapFd(zipFs, fd) {        const remappedFd = this.nextFd++ | ZIP_MAGIC;        this.fdMap.set(remappedFd, [zipFs, fd]);        return remappedFd;    }    async openPromise(p, flags, mode) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.openPromise(p, flags, mode);        }, async (zipFs, { subPath }) => {            return this.remapFd(zipFs, await zipFs.openPromise(subPath, flags, mode));        });    }    openSync(p, flags, mode) {        return this.makeCallSync(p, () => {            return this.baseFs.openSync(p, flags, mode);        }, (zipFs, { subPath }) => {            return this.remapFd(zipFs, zipFs.openSync(subPath, flags, mode));        });    }    async opendirPromise(p, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.opendirPromise(p, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.opendirPromise(subPath, opts);        }, {            requireSubpath: false,        });    }    opendirSync(p, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.opendirSync(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.opendirSync(subPath, opts);        }, {            requireSubpath: false,        });    }    async readPromise(fd, buffer, offset, length, position) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return await this.baseFs.readPromise(fd, buffer, offset, length, position);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`read`);        const [zipFs, realFd] = entry;        return await zipFs.readPromise(realFd, buffer, offset, length, position);    }    readSync(fd, buffer, offset, length, position) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.readSync(fd, buffer, offset, length, position);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`readSync`);        const [zipFs, realFd] = entry;        return zipFs.readSync(realFd, buffer, offset, length, position);    }    async writePromise(fd, buffer, offset, length, position) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC) {            if (typeof buffer === `string`) {                return await this.baseFs.writePromise(fd, buffer, offset);            }            else {                return await this.baseFs.writePromise(fd, buffer, offset, length, position);            }        }        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`write`);        const [zipFs, realFd] = entry;        if (typeof buffer === `string`) {            return await zipFs.writePromise(realFd, buffer, offset);        }        else {            return await zipFs.writePromise(realFd, buffer, offset, length, position);        }    }    writeSync(fd, buffer, offset, length, position) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC) {            if (typeof buffer === `string`) {                return this.baseFs.writeSync(fd, buffer, offset);            }            else {                return this.baseFs.writeSync(fd, buffer, offset, length, position);            }        }        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`writeSync`);        const [zipFs, realFd] = entry;        if (typeof buffer === `string`) {            return zipFs.writeSync(realFd, buffer, offset);        }        else {            return zipFs.writeSync(realFd, buffer, offset, length, position);        }    }    async closePromise(fd) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return await this.baseFs.closePromise(fd);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`close`);        this.fdMap.delete(fd);        const [zipFs, realFd] = entry;        return await zipFs.closePromise(realFd);    }    closeSync(fd) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.closeSync(fd);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`closeSync`);        this.fdMap.delete(fd);        const [zipFs, realFd] = entry;        return zipFs.closeSync(realFd);    }    createReadStream(p, opts) {        if (p === null)            return this.baseFs.createReadStream(p, opts);        return this.makeCallSync(p, () => {            return this.baseFs.createReadStream(p, opts);        }, (zipFs, { archivePath, subPath }) => {            const stream = zipFs.createReadStream(subPath, opts);            // This is a very hacky workaround. `ZipOpenFS` shouldn't have to work with `NativePath`s.            // Ref: https://github.com/yarnpkg/berry/pull/3774            // TODO: think of a better solution            stream.path = path_1.npath.fromPortablePath(this.pathUtils.join(archivePath, subPath));            return stream;        });    }    createWriteStream(p, opts) {        if (p === null)            return this.baseFs.createWriteStream(p, opts);        return this.makeCallSync(p, () => {            return this.baseFs.createWriteStream(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.createWriteStream(subPath, opts);        });    }    async realpathPromise(p) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.realpathPromise(p);        }, async (zipFs, { archivePath, subPath }) => {            let realArchivePath = this.realPaths.get(archivePath);            if (typeof realArchivePath === `undefined`) {                realArchivePath = await this.baseFs.realpathPromise(archivePath);                this.realPaths.set(archivePath, realArchivePath);            }            return this.pathUtils.join(realArchivePath, this.pathUtils.relative(path_1.PortablePath.root, await zipFs.realpathPromise(subPath)));        });    }    realpathSync(p) {        return this.makeCallSync(p, () => {            return this.baseFs.realpathSync(p);        }, (zipFs, { archivePath, subPath }) => {            let realArchivePath = this.realPaths.get(archivePath);            if (typeof realArchivePath === `undefined`) {                realArchivePath = this.baseFs.realpathSync(archivePath);                this.realPaths.set(archivePath, realArchivePath);            }            return this.pathUtils.join(realArchivePath, this.pathUtils.relative(path_1.PortablePath.root, zipFs.realpathSync(subPath)));        });    }    async existsPromise(p) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.existsPromise(p);        }, async (zipFs, { subPath }) => {            return await zipFs.existsPromise(subPath);        });    }    existsSync(p) {        return this.makeCallSync(p, () => {            return this.baseFs.existsSync(p);        }, (zipFs, { subPath }) => {            return zipFs.existsSync(subPath);        });    }    async accessPromise(p, mode) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.accessPromise(p, mode);        }, async (zipFs, { subPath }) => {            return await zipFs.accessPromise(subPath, mode);        });    }    accessSync(p, mode) {        return this.makeCallSync(p, () => {            return this.baseFs.accessSync(p, mode);        }, (zipFs, { subPath }) => {            return zipFs.accessSync(subPath, mode);        });    }    async statPromise(p, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.statPromise(p, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.statPromise(subPath, opts);        });    }    statSync(p, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.statSync(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.statSync(subPath, opts);        });    }    async fstatPromise(fd, opts) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.fstatPromise(fd, opts);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`fstat`);        const [zipFs, realFd] = entry;        return zipFs.fstatPromise(realFd, opts);    }    fstatSync(fd, opts) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.fstatSync(fd, opts);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`fstatSync`);        const [zipFs, realFd] = entry;        return zipFs.fstatSync(realFd, opts);    }    async lstatPromise(p, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.lstatPromise(p, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.lstatPromise(subPath, opts);        });    }    lstatSync(p, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.lstatSync(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.lstatSync(subPath, opts);        });    }    async fchmodPromise(fd, mask) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.fchmodPromise(fd, mask);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`fchmod`);        const [zipFs, realFd] = entry;        return zipFs.fchmodPromise(realFd, mask);    }    fchmodSync(fd, mask) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.fchmodSync(fd, mask);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`fchmodSync`);        const [zipFs, realFd] = entry;        return zipFs.fchmodSync(realFd, mask);    }    async chmodPromise(p, mask) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.chmodPromise(p, mask);        }, async (zipFs, { subPath }) => {            return await zipFs.chmodPromise(subPath, mask);        });    }    chmodSync(p, mask) {        return this.makeCallSync(p, () => {            return this.baseFs.chmodSync(p, mask);        }, (zipFs, { subPath }) => {            return zipFs.chmodSync(subPath, mask);        });    }    async fchownPromise(fd, uid, gid) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.fchownPromise(fd, uid, gid);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`fchown`);        const [zipFs, realFd] = entry;        return zipFs.fchownPromise(realFd, uid, gid);    }    fchownSync(fd, uid, gid) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.fchownSync(fd, uid, gid);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`fchownSync`);        const [zipFs, realFd] = entry;        return zipFs.fchownSync(realFd, uid, gid);    }    async chownPromise(p, uid, gid) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.chownPromise(p, uid, gid);        }, async (zipFs, { subPath }) => {            return await zipFs.chownPromise(subPath, uid, gid);        });    }    chownSync(p, uid, gid) {        return this.makeCallSync(p, () => {            return this.baseFs.chownSync(p, uid, gid);        }, (zipFs, { subPath }) => {            return zipFs.chownSync(subPath, uid, gid);        });    }    async renamePromise(oldP, newP) {        return await this.makeCallPromise(oldP, async () => {            return await this.makeCallPromise(newP, async () => {                return await this.baseFs.renamePromise(oldP, newP);            }, async () => {                throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });            });        }, async (zipFsO, { subPath: subPathO }) => {            return await this.makeCallPromise(newP, async () => {                throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });            }, async (zipFsN, { subPath: subPathN }) => {                if (zipFsO !== zipFsN) {                    throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });                }                else {                    return await zipFsO.renamePromise(subPathO, subPathN);                }            });        });    }    renameSync(oldP, newP) {        return this.makeCallSync(oldP, () => {            return this.makeCallSync(newP, () => {                return this.baseFs.renameSync(oldP, newP);            }, () => {                throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });            });        }, (zipFsO, { subPath: subPathO }) => {            return this.makeCallSync(newP, () => {                throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });            }, (zipFsN, { subPath: subPathN }) => {                if (zipFsO !== zipFsN) {                    throw Object.assign(new Error(`EEXDEV: cross-device link not permitted`), { code: `EEXDEV` });                }                else {                    return zipFsO.renameSync(subPathO, subPathN);                }            });        });    }    async copyFilePromise(sourceP, destP, flags = 0) {        const fallback = async (sourceFs, sourceP, destFs, destP) => {            if ((flags & fs_1.constants.COPYFILE_FICLONE_FORCE) !== 0)                throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`), { code: `EXDEV` });            if ((flags & fs_1.constants.COPYFILE_EXCL) && await this.existsPromise(sourceP))                throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`), { code: `EEXIST` });            let content;            try {                content = await sourceFs.readFilePromise(sourceP);            }            catch (error) {                throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`), { code: `EINVAL` });            }            await destFs.writeFilePromise(destP, content);        };        return await this.makeCallPromise(sourceP, async () => {            return await this.makeCallPromise(destP, async () => {                return await this.baseFs.copyFilePromise(sourceP, destP, flags);            }, async (zipFsD, { subPath: subPathD }) => {                return await fallback(this.baseFs, sourceP, zipFsD, subPathD);            });        }, async (zipFsS, { subPath: subPathS }) => {            return await this.makeCallPromise(destP, async () => {                return await fallback(zipFsS, subPathS, this.baseFs, destP);            }, async (zipFsD, { subPath: subPathD }) => {                if (zipFsS !== zipFsD) {                    return await fallback(zipFsS, subPathS, zipFsD, subPathD);                }                else {                    return await zipFsS.copyFilePromise(subPathS, subPathD, flags);                }            });        });    }    copyFileSync(sourceP, destP, flags = 0) {        const fallback = (sourceFs, sourceP, destFs, destP) => {            if ((flags & fs_1.constants.COPYFILE_FICLONE_FORCE) !== 0)                throw Object.assign(new Error(`EXDEV: cross-device clone not permitted, copyfile '${sourceP}' -> ${destP}'`), { code: `EXDEV` });            if ((flags & fs_1.constants.COPYFILE_EXCL) && this.existsSync(sourceP))                throw Object.assign(new Error(`EEXIST: file already exists, copyfile '${sourceP}' -> '${destP}'`), { code: `EEXIST` });            let content;            try {                content = sourceFs.readFileSync(sourceP);            }            catch (error) {                throw Object.assign(new Error(`EINVAL: invalid argument, copyfile '${sourceP}' -> '${destP}'`), { code: `EINVAL` });            }            destFs.writeFileSync(destP, content);        };        return this.makeCallSync(sourceP, () => {            return this.makeCallSync(destP, () => {                return this.baseFs.copyFileSync(sourceP, destP, flags);            }, (zipFsD, { subPath: subPathD }) => {                return fallback(this.baseFs, sourceP, zipFsD, subPathD);            });        }, (zipFsS, { subPath: subPathS }) => {            return this.makeCallSync(destP, () => {                return fallback(zipFsS, subPathS, this.baseFs, destP);            }, (zipFsD, { subPath: subPathD }) => {                if (zipFsS !== zipFsD) {                    return fallback(zipFsS, subPathS, zipFsD, subPathD);                }                else {                    return zipFsS.copyFileSync(subPathS, subPathD, flags);                }            });        });    }    async appendFilePromise(p, content, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.appendFilePromise(p, content, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.appendFilePromise(subPath, content, opts);        });    }    appendFileSync(p, content, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.appendFileSync(p, content, opts);        }, (zipFs, { subPath }) => {            return zipFs.appendFileSync(subPath, content, opts);        });    }    async writeFilePromise(p, content, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.writeFilePromise(p, content, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.writeFilePromise(subPath, content, opts);        });    }    writeFileSync(p, content, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.writeFileSync(p, content, opts);        }, (zipFs, { subPath }) => {            return zipFs.writeFileSync(subPath, content, opts);        });    }    async unlinkPromise(p) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.unlinkPromise(p);        }, async (zipFs, { subPath }) => {            return await zipFs.unlinkPromise(subPath);        });    }    unlinkSync(p) {        return this.makeCallSync(p, () => {            return this.baseFs.unlinkSync(p);        }, (zipFs, { subPath }) => {            return zipFs.unlinkSync(subPath);        });    }    async utimesPromise(p, atime, mtime) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.utimesPromise(p, atime, mtime);        }, async (zipFs, { subPath }) => {            return await zipFs.utimesPromise(subPath, atime, mtime);        });    }    utimesSync(p, atime, mtime) {        return this.makeCallSync(p, () => {            return this.baseFs.utimesSync(p, atime, mtime);        }, (zipFs, { subPath }) => {            return zipFs.utimesSync(subPath, atime, mtime);        });    }    async mkdirPromise(p, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.mkdirPromise(p, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.mkdirPromise(subPath, opts);        });    }    mkdirSync(p, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.mkdirSync(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.mkdirSync(subPath, opts);        });    }    async rmdirPromise(p, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.rmdirPromise(p, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.rmdirPromise(subPath, opts);        });    }    rmdirSync(p, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.rmdirSync(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.rmdirSync(subPath, opts);        });    }    async linkPromise(existingP, newP) {        return await this.makeCallPromise(newP, async () => {            return await this.baseFs.linkPromise(existingP, newP);        }, async (zipFs, { subPath }) => {            return await zipFs.linkPromise(existingP, subPath);        });    }    linkSync(existingP, newP) {        return this.makeCallSync(newP, () => {            return this.baseFs.linkSync(existingP, newP);        }, (zipFs, { subPath }) => {            return zipFs.linkSync(existingP, subPath);        });    }    async symlinkPromise(target, p, type) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.symlinkPromise(target, p, type);        }, async (zipFs, { subPath }) => {            return await zipFs.symlinkPromise(target, subPath);        });    }    symlinkSync(target, p, type) {        return this.makeCallSync(p, () => {            return this.baseFs.symlinkSync(target, p, type);        }, (zipFs, { subPath }) => {            return zipFs.symlinkSync(target, subPath);        });    }    async readFilePromise(p, encoding) {        return this.makeCallPromise(p, async () => {            // This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered)            switch (encoding) {                case `utf8`:                    return await this.baseFs.readFilePromise(p, encoding);                default:                    return await this.baseFs.readFilePromise(p, encoding);            }        }, async (zipFs, { subPath }) => {            return await zipFs.readFilePromise(subPath, encoding);        });    }    readFileSync(p, encoding) {        return this.makeCallSync(p, () => {            // This weird switch is required to tell TypeScript that the signatures are proper (otherwise it thinks that only the generic one is covered)            switch (encoding) {                case `utf8`:                    return this.baseFs.readFileSync(p, encoding);                default:                    return this.baseFs.readFileSync(p, encoding);            }        }, (zipFs, { subPath }) => {            return zipFs.readFileSync(subPath, encoding);        });    }    async readdirPromise(p, opts) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.readdirPromise(p, opts);        }, async (zipFs, { subPath }) => {            return await zipFs.readdirPromise(subPath, opts);        }, {            requireSubpath: false,        });    }    readdirSync(p, opts) {        return this.makeCallSync(p, () => {            return this.baseFs.readdirSync(p, opts);        }, (zipFs, { subPath }) => {            return zipFs.readdirSync(subPath, opts);        }, {            requireSubpath: false,        });    }    async readlinkPromise(p) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.readlinkPromise(p);        }, async (zipFs, { subPath }) => {            return await zipFs.readlinkPromise(subPath);        });    }    readlinkSync(p) {        return this.makeCallSync(p, () => {            return this.baseFs.readlinkSync(p);        }, (zipFs, { subPath }) => {            return zipFs.readlinkSync(subPath);        });    }    async truncatePromise(p, len) {        return await this.makeCallPromise(p, async () => {            return await this.baseFs.truncatePromise(p, len);        }, async (zipFs, { subPath }) => {            return await zipFs.truncatePromise(subPath, len);        });    }    truncateSync(p, len) {        return this.makeCallSync(p, () => {            return this.baseFs.truncateSync(p, len);        }, (zipFs, { subPath }) => {            return zipFs.truncateSync(subPath, len);        });    }    async ftruncatePromise(fd, len) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.ftruncatePromise(fd, len);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`ftruncate`);        const [zipFs, realFd] = entry;        return zipFs.ftruncatePromise(realFd, len);    }    ftruncateSync(fd, len) {        if ((fd & ZIP_MASK) !== ZIP_MAGIC)            return this.baseFs.ftruncateSync(fd, len);        const entry = this.fdMap.get(fd);        if (typeof entry === `undefined`)            throw errors.EBADF(`ftruncateSync`);        const [zipFs, realFd] = entry;        return zipFs.ftruncateSync(realFd, len);    }    watch(p, a, b) {        return this.makeCallSync(p, () => {            return this.baseFs.watch(p,             // @ts-expect-error            a, b);        }, (zipFs, { subPath }) => {            return zipFs.watch(subPath,             // @ts-expect-error            a, b);        });    }    watchFile(p, a, b) {        return this.makeCallSync(p, () => {            return this.baseFs.watchFile(p,             // @ts-expect-error            a, b);        }, () => {            return (0, watchFile_1.watchFile)(this, p, a, b);        });    }    unwatchFile(p, cb) {        return this.makeCallSync(p, () => {            return this.baseFs.unwatchFile(p, cb);        }, () => {            return (0, watchFile_1.unwatchFile)(this, p, cb);        });    }    async makeCallPromise(p, discard, accept, { requireSubpath = true } = {}) {        if (typeof p !== `string`)            return await discard();        const normalizedP = this.resolve(p);        const zipInfo = this.findZip(normalizedP);        if (!zipInfo)            return await discard();        if (requireSubpath && zipInfo.subPath === `/`)            return await discard();        return await this.getZipPromise(zipInfo.archivePath, async (zipFs) => await accept(zipFs, zipInfo));    }    makeCallSync(p, discard, accept, { requireSubpath = true } = {}) {        if (typeof p !== `string`)            return discard();        const normalizedP = this.resolve(p);        const zipInfo = this.findZip(normalizedP);        if (!zipInfo)            return discard();        if (requireSubpath && zipInfo.subPath === `/`)            return discard();        return this.getZipSync(zipInfo.archivePath, zipFs => accept(zipFs, zipInfo));    }    findZip(p) {        if (this.filter && !this.filter.test(p))            return null;        let filePath = ``;        while (true) {            const pathPartWithArchive = p.substring(filePath.length);            let archivePart;            if (!this.fileExtensions) {                archivePart = (0, exports.getArchivePart)(pathPartWithArchive, `.zip`);            }            else {                for (const ext of this.fileExtensions) {                    archivePart = (0, exports.getArchivePart)(pathPartWithArchive, ext);                    if (archivePart) {                        break;                    }                }            }            if (!archivePart)                return null;            filePath = this.pathUtils.join(filePath, archivePart);            if (this.isZip.has(filePath) === false) {                if (this.notZip.has(filePath))                    continue;                try {                    if (!this.baseFs.lstatSync(filePath).isFile()) {                        this.notZip.add(filePath);                        continue;                    }                }                catch {                    return null;                }                this.isZip.add(filePath);            }            return {                archivePath: filePath,                subPath: this.pathUtils.join(path_1.PortablePath.root, p.substring(filePath.length)),            };        }    }    limitOpenFiles(max) {        if (this.zipInstances === null)            return;        const now = Date.now();        let nextExpiresAt = now + this.maxAge;        let closeCount = max === null ? 0 : this.zipInstances.size - max;        for (const [path, { zipFs, expiresAt, refCount }] of this.zipInstances.entries()) {            if (refCount !== 0 || zipFs.hasOpenFileHandles()) {                continue;            }            else if (now >= expiresAt) {                zipFs.saveAndClose();                this.zipInstances.delete(path);                closeCount -= 1;                continue;            }            else if (max === null || closeCount <= 0) {                nextExpiresAt = expiresAt;                break;            }            zipFs.saveAndClose();            this.zipInstances.delete(path);            closeCount -= 1;        }        if (this.limitOpenFilesTimeout === null && ((max === null && this.zipInstances.size > 0) || max !== null)) {            this.limitOpenFilesTimeout = setTimeout(() => {                this.limitOpenFilesTimeout = null;                this.limitOpenFiles(null);            }, nextExpiresAt - now).unref();        }    }    async getZipPromise(p, accept) {        const getZipOptions = async () => ({            baseFs: this.baseFs,            libzip: this.libzip,            readOnly: this.readOnlyArchives,            stats: await this.baseFs.statPromise(p),        });        if (this.zipInstances) {            let cachedZipFs = this.zipInstances.get(p);            if (!cachedZipFs) {                const zipOptions = await getZipOptions();                // We need to recheck because concurrent getZipPromise calls may                // have instantiated the zip archive while we were waiting                cachedZipFs = this.zipInstances.get(p);                if (!cachedZipFs) {                    cachedZipFs = {                        zipFs: new ZipFS_1.ZipFS(p, zipOptions),                        expiresAt: 0,                        refCount: 0,                    };                }            }            // Removing then re-adding the field allows us to easily implement            // a basic LRU garbage collection strategy            this.zipInstances.delete(p);            this.limitOpenFiles(this.maxOpenFiles - 1);            this.zipInstances.set(p, cachedZipFs);            cachedZipFs.expiresAt = Date.now() + this.maxAge;            cachedZipFs.refCount += 1;            try {                return await accept(cachedZipFs.zipFs);            }            finally {                cachedZipFs.refCount -= 1;            }        }        else {            const zipFs = new ZipFS_1.ZipFS(p, await getZipOptions());            try {                return await accept(zipFs);            }            finally {                zipFs.saveAndClose();            }        }    }    getZipSync(p, accept) {        const getZipOptions = () => ({            baseFs: this.baseFs,            libzip: this.libzip,            readOnly: this.readOnlyArchives,            stats: this.baseFs.statSync(p),        });        if (this.zipInstances) {            let cachedZipFs = this.zipInstances.get(p);            if (!cachedZipFs) {                cachedZipFs = {                    zipFs: new ZipFS_1.ZipFS(p, getZipOptions()),                    expiresAt: 0,                    refCount: 0,                };            }            // Removing then re-adding the field allows us to easily implement            // a basic LRU garbage collection strategy            this.zipInstances.delete(p);            this.limitOpenFiles(this.maxOpenFiles - 1);            this.zipInstances.set(p, cachedZipFs);            cachedZipFs.expiresAt = Date.now() + this.maxAge;            return accept(cachedZipFs.zipFs);        }        else {            const zipFs = new ZipFS_1.ZipFS(p, getZipOptions());            try {                return accept(zipFs);            }            finally {                zipFs.saveAndClose();            }        }    }}exports.ZipOpenFS = ZipOpenFS;
 |